162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2006 Intel Corporation. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This software is available to you under a choice of one of two 562306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 862306a36Sopenharmony_ci * OpenIB.org BSD license below: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1162306a36Sopenharmony_ci * without modification, are permitted provided that the following 1262306a36Sopenharmony_ci * conditions are met: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1562306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1662306a36Sopenharmony_ci * disclaimer. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 1962306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2062306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2162306a36Sopenharmony_ci * provided with the distribution. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3062306a36Sopenharmony_ci * SOFTWARE. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <linux/completion.h> 3462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 3562306a36Sopenharmony_ci#include <linux/err.h> 3662306a36Sopenharmony_ci#include <linux/interrupt.h> 3762306a36Sopenharmony_ci#include <linux/export.h> 3862306a36Sopenharmony_ci#include <linux/slab.h> 3962306a36Sopenharmony_ci#include <linux/bitops.h> 4062306a36Sopenharmony_ci#include <linux/random.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include <rdma/ib_cache.h> 4362306a36Sopenharmony_ci#include "sa.h" 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int mcast_add_one(struct ib_device *device); 4662306a36Sopenharmony_cistatic void mcast_remove_one(struct ib_device *device, void *client_data); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct ib_client mcast_client = { 4962306a36Sopenharmony_ci .name = "ib_multicast", 5062306a36Sopenharmony_ci .add = mcast_add_one, 5162306a36Sopenharmony_ci .remove = mcast_remove_one 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic struct ib_sa_client sa_client; 5562306a36Sopenharmony_cistatic struct workqueue_struct *mcast_wq; 5662306a36Sopenharmony_cistatic union ib_gid mgid0; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct mcast_device; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct mcast_port { 6162306a36Sopenharmony_ci struct mcast_device *dev; 6262306a36Sopenharmony_ci spinlock_t lock; 6362306a36Sopenharmony_ci struct rb_root table; 6462306a36Sopenharmony_ci refcount_t refcount; 6562306a36Sopenharmony_ci struct completion comp; 6662306a36Sopenharmony_ci u32 port_num; 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistruct mcast_device { 7062306a36Sopenharmony_ci struct ib_device *device; 7162306a36Sopenharmony_ci struct ib_event_handler event_handler; 7262306a36Sopenharmony_ci int start_port; 7362306a36Sopenharmony_ci int end_port; 7462306a36Sopenharmony_ci struct mcast_port port[]; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cienum mcast_state { 7862306a36Sopenharmony_ci MCAST_JOINING, 7962306a36Sopenharmony_ci MCAST_MEMBER, 8062306a36Sopenharmony_ci MCAST_ERROR, 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cienum mcast_group_state { 8462306a36Sopenharmony_ci MCAST_IDLE, 8562306a36Sopenharmony_ci MCAST_BUSY, 8662306a36Sopenharmony_ci MCAST_GROUP_ERROR, 8762306a36Sopenharmony_ci MCAST_PKEY_EVENT 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cienum { 9162306a36Sopenharmony_ci MCAST_INVALID_PKEY_INDEX = 0xFFFF 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistruct mcast_member; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistruct mcast_group { 9762306a36Sopenharmony_ci struct ib_sa_mcmember_rec rec; 9862306a36Sopenharmony_ci struct rb_node node; 9962306a36Sopenharmony_ci struct mcast_port *port; 10062306a36Sopenharmony_ci spinlock_t lock; 10162306a36Sopenharmony_ci struct work_struct work; 10262306a36Sopenharmony_ci struct list_head pending_list; 10362306a36Sopenharmony_ci struct list_head active_list; 10462306a36Sopenharmony_ci struct mcast_member *last_join; 10562306a36Sopenharmony_ci int members[NUM_JOIN_MEMBERSHIP_TYPES]; 10662306a36Sopenharmony_ci atomic_t refcount; 10762306a36Sopenharmony_ci enum mcast_group_state state; 10862306a36Sopenharmony_ci struct ib_sa_query *query; 10962306a36Sopenharmony_ci u16 pkey_index; 11062306a36Sopenharmony_ci u8 leave_state; 11162306a36Sopenharmony_ci int retries; 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistruct mcast_member { 11562306a36Sopenharmony_ci struct ib_sa_multicast multicast; 11662306a36Sopenharmony_ci struct ib_sa_client *client; 11762306a36Sopenharmony_ci struct mcast_group *group; 11862306a36Sopenharmony_ci struct list_head list; 11962306a36Sopenharmony_ci enum mcast_state state; 12062306a36Sopenharmony_ci refcount_t refcount; 12162306a36Sopenharmony_ci struct completion comp; 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void join_handler(int status, struct ib_sa_mcmember_rec *rec, 12562306a36Sopenharmony_ci void *context); 12662306a36Sopenharmony_cistatic void leave_handler(int status, struct ib_sa_mcmember_rec *rec, 12762306a36Sopenharmony_ci void *context); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic struct mcast_group *mcast_find(struct mcast_port *port, 13062306a36Sopenharmony_ci union ib_gid *mgid) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct rb_node *node = port->table.rb_node; 13362306a36Sopenharmony_ci struct mcast_group *group; 13462306a36Sopenharmony_ci int ret; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci while (node) { 13762306a36Sopenharmony_ci group = rb_entry(node, struct mcast_group, node); 13862306a36Sopenharmony_ci ret = memcmp(mgid->raw, group->rec.mgid.raw, sizeof *mgid); 13962306a36Sopenharmony_ci if (!ret) 14062306a36Sopenharmony_ci return group; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (ret < 0) 14362306a36Sopenharmony_ci node = node->rb_left; 14462306a36Sopenharmony_ci else 14562306a36Sopenharmony_ci node = node->rb_right; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci return NULL; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic struct mcast_group *mcast_insert(struct mcast_port *port, 15162306a36Sopenharmony_ci struct mcast_group *group, 15262306a36Sopenharmony_ci int allow_duplicates) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct rb_node **link = &port->table.rb_node; 15562306a36Sopenharmony_ci struct rb_node *parent = NULL; 15662306a36Sopenharmony_ci struct mcast_group *cur_group; 15762306a36Sopenharmony_ci int ret; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci while (*link) { 16062306a36Sopenharmony_ci parent = *link; 16162306a36Sopenharmony_ci cur_group = rb_entry(parent, struct mcast_group, node); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ret = memcmp(group->rec.mgid.raw, cur_group->rec.mgid.raw, 16462306a36Sopenharmony_ci sizeof group->rec.mgid); 16562306a36Sopenharmony_ci if (ret < 0) 16662306a36Sopenharmony_ci link = &(*link)->rb_left; 16762306a36Sopenharmony_ci else if (ret > 0) 16862306a36Sopenharmony_ci link = &(*link)->rb_right; 16962306a36Sopenharmony_ci else if (allow_duplicates) 17062306a36Sopenharmony_ci link = &(*link)->rb_left; 17162306a36Sopenharmony_ci else 17262306a36Sopenharmony_ci return cur_group; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci rb_link_node(&group->node, parent, link); 17562306a36Sopenharmony_ci rb_insert_color(&group->node, &port->table); 17662306a36Sopenharmony_ci return NULL; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void deref_port(struct mcast_port *port) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci if (refcount_dec_and_test(&port->refcount)) 18262306a36Sopenharmony_ci complete(&port->comp); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic void release_group(struct mcast_group *group) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct mcast_port *port = group->port; 18862306a36Sopenharmony_ci unsigned long flags; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 19162306a36Sopenharmony_ci if (atomic_dec_and_test(&group->refcount)) { 19262306a36Sopenharmony_ci rb_erase(&group->node, &port->table); 19362306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 19462306a36Sopenharmony_ci kfree(group); 19562306a36Sopenharmony_ci deref_port(port); 19662306a36Sopenharmony_ci } else 19762306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void deref_member(struct mcast_member *member) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci if (refcount_dec_and_test(&member->refcount)) 20362306a36Sopenharmony_ci complete(&member->comp); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void queue_join(struct mcast_member *member) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct mcast_group *group = member->group; 20962306a36Sopenharmony_ci unsigned long flags; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci spin_lock_irqsave(&group->lock, flags); 21262306a36Sopenharmony_ci list_add_tail(&member->list, &group->pending_list); 21362306a36Sopenharmony_ci if (group->state == MCAST_IDLE) { 21462306a36Sopenharmony_ci group->state = MCAST_BUSY; 21562306a36Sopenharmony_ci atomic_inc(&group->refcount); 21662306a36Sopenharmony_ci queue_work(mcast_wq, &group->work); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci spin_unlock_irqrestore(&group->lock, flags); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* 22262306a36Sopenharmony_ci * A multicast group has four types of members: full member, non member, 22362306a36Sopenharmony_ci * sendonly non member and sendonly full member. 22462306a36Sopenharmony_ci * We need to keep track of the number of members of each 22562306a36Sopenharmony_ci * type based on their join state. Adjust the number of members the belong to 22662306a36Sopenharmony_ci * the specified join states. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_cistatic void adjust_membership(struct mcast_group *group, u8 join_state, int inc) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci int i; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci for (i = 0; i < NUM_JOIN_MEMBERSHIP_TYPES; i++, join_state >>= 1) 23362306a36Sopenharmony_ci if (join_state & 0x1) 23462306a36Sopenharmony_ci group->members[i] += inc; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* 23862306a36Sopenharmony_ci * If a multicast group has zero members left for a particular join state, but 23962306a36Sopenharmony_ci * the group is still a member with the SA, we need to leave that join state. 24062306a36Sopenharmony_ci * Determine which join states we still belong to, but that do not have any 24162306a36Sopenharmony_ci * active members. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_cistatic u8 get_leave_state(struct mcast_group *group) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci u8 leave_state = 0; 24662306a36Sopenharmony_ci int i; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci for (i = 0; i < NUM_JOIN_MEMBERSHIP_TYPES; i++) 24962306a36Sopenharmony_ci if (!group->members[i]) 25062306a36Sopenharmony_ci leave_state |= (0x1 << i); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return leave_state & group->rec.join_state; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic int check_selector(ib_sa_comp_mask comp_mask, 25662306a36Sopenharmony_ci ib_sa_comp_mask selector_mask, 25762306a36Sopenharmony_ci ib_sa_comp_mask value_mask, 25862306a36Sopenharmony_ci u8 selector, u8 src_value, u8 dst_value) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci int err; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (!(comp_mask & selector_mask) || !(comp_mask & value_mask)) 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci switch (selector) { 26662306a36Sopenharmony_ci case IB_SA_GT: 26762306a36Sopenharmony_ci err = (src_value <= dst_value); 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci case IB_SA_LT: 27062306a36Sopenharmony_ci err = (src_value >= dst_value); 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci case IB_SA_EQ: 27362306a36Sopenharmony_ci err = (src_value != dst_value); 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci default: 27662306a36Sopenharmony_ci err = 0; 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return err; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int cmp_rec(struct ib_sa_mcmember_rec *src, 28462306a36Sopenharmony_ci struct ib_sa_mcmember_rec *dst, ib_sa_comp_mask comp_mask) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci /* MGID must already match */ 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (comp_mask & IB_SA_MCMEMBER_REC_PORT_GID && 28962306a36Sopenharmony_ci memcmp(&src->port_gid, &dst->port_gid, sizeof src->port_gid)) 29062306a36Sopenharmony_ci return -EINVAL; 29162306a36Sopenharmony_ci if (comp_mask & IB_SA_MCMEMBER_REC_QKEY && src->qkey != dst->qkey) 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci if (comp_mask & IB_SA_MCMEMBER_REC_MLID && src->mlid != dst->mlid) 29462306a36Sopenharmony_ci return -EINVAL; 29562306a36Sopenharmony_ci if (check_selector(comp_mask, IB_SA_MCMEMBER_REC_MTU_SELECTOR, 29662306a36Sopenharmony_ci IB_SA_MCMEMBER_REC_MTU, dst->mtu_selector, 29762306a36Sopenharmony_ci src->mtu, dst->mtu)) 29862306a36Sopenharmony_ci return -EINVAL; 29962306a36Sopenharmony_ci if (comp_mask & IB_SA_MCMEMBER_REC_TRAFFIC_CLASS && 30062306a36Sopenharmony_ci src->traffic_class != dst->traffic_class) 30162306a36Sopenharmony_ci return -EINVAL; 30262306a36Sopenharmony_ci if (comp_mask & IB_SA_MCMEMBER_REC_PKEY && src->pkey != dst->pkey) 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci if (check_selector(comp_mask, IB_SA_MCMEMBER_REC_RATE_SELECTOR, 30562306a36Sopenharmony_ci IB_SA_MCMEMBER_REC_RATE, dst->rate_selector, 30662306a36Sopenharmony_ci src->rate, dst->rate)) 30762306a36Sopenharmony_ci return -EINVAL; 30862306a36Sopenharmony_ci if (check_selector(comp_mask, 30962306a36Sopenharmony_ci IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME_SELECTOR, 31062306a36Sopenharmony_ci IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME, 31162306a36Sopenharmony_ci dst->packet_life_time_selector, 31262306a36Sopenharmony_ci src->packet_life_time, dst->packet_life_time)) 31362306a36Sopenharmony_ci return -EINVAL; 31462306a36Sopenharmony_ci if (comp_mask & IB_SA_MCMEMBER_REC_SL && src->sl != dst->sl) 31562306a36Sopenharmony_ci return -EINVAL; 31662306a36Sopenharmony_ci if (comp_mask & IB_SA_MCMEMBER_REC_FLOW_LABEL && 31762306a36Sopenharmony_ci src->flow_label != dst->flow_label) 31862306a36Sopenharmony_ci return -EINVAL; 31962306a36Sopenharmony_ci if (comp_mask & IB_SA_MCMEMBER_REC_HOP_LIMIT && 32062306a36Sopenharmony_ci src->hop_limit != dst->hop_limit) 32162306a36Sopenharmony_ci return -EINVAL; 32262306a36Sopenharmony_ci if (comp_mask & IB_SA_MCMEMBER_REC_SCOPE && src->scope != dst->scope) 32362306a36Sopenharmony_ci return -EINVAL; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* join_state checked separately, proxy_join ignored */ 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return 0; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int send_join(struct mcast_group *group, struct mcast_member *member) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct mcast_port *port = group->port; 33362306a36Sopenharmony_ci int ret; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci group->last_join = member; 33662306a36Sopenharmony_ci ret = ib_sa_mcmember_rec_query(&sa_client, port->dev->device, 33762306a36Sopenharmony_ci port->port_num, IB_MGMT_METHOD_SET, 33862306a36Sopenharmony_ci &member->multicast.rec, 33962306a36Sopenharmony_ci member->multicast.comp_mask, 34062306a36Sopenharmony_ci 3000, GFP_KERNEL, join_handler, group, 34162306a36Sopenharmony_ci &group->query); 34262306a36Sopenharmony_ci return (ret > 0) ? 0 : ret; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int send_leave(struct mcast_group *group, u8 leave_state) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct mcast_port *port = group->port; 34862306a36Sopenharmony_ci struct ib_sa_mcmember_rec rec; 34962306a36Sopenharmony_ci int ret; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci rec = group->rec; 35262306a36Sopenharmony_ci rec.join_state = leave_state; 35362306a36Sopenharmony_ci group->leave_state = leave_state; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci ret = ib_sa_mcmember_rec_query(&sa_client, port->dev->device, 35662306a36Sopenharmony_ci port->port_num, IB_SA_METHOD_DELETE, &rec, 35762306a36Sopenharmony_ci IB_SA_MCMEMBER_REC_MGID | 35862306a36Sopenharmony_ci IB_SA_MCMEMBER_REC_PORT_GID | 35962306a36Sopenharmony_ci IB_SA_MCMEMBER_REC_JOIN_STATE, 36062306a36Sopenharmony_ci 3000, GFP_KERNEL, leave_handler, 36162306a36Sopenharmony_ci group, &group->query); 36262306a36Sopenharmony_ci return (ret > 0) ? 0 : ret; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic void join_group(struct mcast_group *group, struct mcast_member *member, 36662306a36Sopenharmony_ci u8 join_state) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci member->state = MCAST_MEMBER; 36962306a36Sopenharmony_ci adjust_membership(group, join_state, 1); 37062306a36Sopenharmony_ci group->rec.join_state |= join_state; 37162306a36Sopenharmony_ci member->multicast.rec = group->rec; 37262306a36Sopenharmony_ci member->multicast.rec.join_state = join_state; 37362306a36Sopenharmony_ci list_move(&member->list, &group->active_list); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int fail_join(struct mcast_group *group, struct mcast_member *member, 37762306a36Sopenharmony_ci int status) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci spin_lock_irq(&group->lock); 38062306a36Sopenharmony_ci list_del_init(&member->list); 38162306a36Sopenharmony_ci spin_unlock_irq(&group->lock); 38262306a36Sopenharmony_ci return member->multicast.callback(status, &member->multicast); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic void process_group_error(struct mcast_group *group) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct mcast_member *member; 38862306a36Sopenharmony_ci int ret = 0; 38962306a36Sopenharmony_ci u16 pkey_index; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (group->state == MCAST_PKEY_EVENT) 39262306a36Sopenharmony_ci ret = ib_find_pkey(group->port->dev->device, 39362306a36Sopenharmony_ci group->port->port_num, 39462306a36Sopenharmony_ci be16_to_cpu(group->rec.pkey), &pkey_index); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci spin_lock_irq(&group->lock); 39762306a36Sopenharmony_ci if (group->state == MCAST_PKEY_EVENT && !ret && 39862306a36Sopenharmony_ci group->pkey_index == pkey_index) 39962306a36Sopenharmony_ci goto out; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci while (!list_empty(&group->active_list)) { 40262306a36Sopenharmony_ci member = list_entry(group->active_list.next, 40362306a36Sopenharmony_ci struct mcast_member, list); 40462306a36Sopenharmony_ci refcount_inc(&member->refcount); 40562306a36Sopenharmony_ci list_del_init(&member->list); 40662306a36Sopenharmony_ci adjust_membership(group, member->multicast.rec.join_state, -1); 40762306a36Sopenharmony_ci member->state = MCAST_ERROR; 40862306a36Sopenharmony_ci spin_unlock_irq(&group->lock); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci ret = member->multicast.callback(-ENETRESET, 41162306a36Sopenharmony_ci &member->multicast); 41262306a36Sopenharmony_ci deref_member(member); 41362306a36Sopenharmony_ci if (ret) 41462306a36Sopenharmony_ci ib_sa_free_multicast(&member->multicast); 41562306a36Sopenharmony_ci spin_lock_irq(&group->lock); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci group->rec.join_state = 0; 41962306a36Sopenharmony_ciout: 42062306a36Sopenharmony_ci group->state = MCAST_BUSY; 42162306a36Sopenharmony_ci spin_unlock_irq(&group->lock); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic void mcast_work_handler(struct work_struct *work) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct mcast_group *group; 42762306a36Sopenharmony_ci struct mcast_member *member; 42862306a36Sopenharmony_ci struct ib_sa_multicast *multicast; 42962306a36Sopenharmony_ci int status, ret; 43062306a36Sopenharmony_ci u8 join_state; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci group = container_of(work, typeof(*group), work); 43362306a36Sopenharmony_ciretest: 43462306a36Sopenharmony_ci spin_lock_irq(&group->lock); 43562306a36Sopenharmony_ci while (!list_empty(&group->pending_list) || 43662306a36Sopenharmony_ci (group->state != MCAST_BUSY)) { 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (group->state != MCAST_BUSY) { 43962306a36Sopenharmony_ci spin_unlock_irq(&group->lock); 44062306a36Sopenharmony_ci process_group_error(group); 44162306a36Sopenharmony_ci goto retest; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci member = list_entry(group->pending_list.next, 44562306a36Sopenharmony_ci struct mcast_member, list); 44662306a36Sopenharmony_ci multicast = &member->multicast; 44762306a36Sopenharmony_ci join_state = multicast->rec.join_state; 44862306a36Sopenharmony_ci refcount_inc(&member->refcount); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (join_state == (group->rec.join_state & join_state)) { 45162306a36Sopenharmony_ci status = cmp_rec(&group->rec, &multicast->rec, 45262306a36Sopenharmony_ci multicast->comp_mask); 45362306a36Sopenharmony_ci if (!status) 45462306a36Sopenharmony_ci join_group(group, member, join_state); 45562306a36Sopenharmony_ci else 45662306a36Sopenharmony_ci list_del_init(&member->list); 45762306a36Sopenharmony_ci spin_unlock_irq(&group->lock); 45862306a36Sopenharmony_ci ret = multicast->callback(status, multicast); 45962306a36Sopenharmony_ci } else { 46062306a36Sopenharmony_ci spin_unlock_irq(&group->lock); 46162306a36Sopenharmony_ci status = send_join(group, member); 46262306a36Sopenharmony_ci if (!status) { 46362306a36Sopenharmony_ci deref_member(member); 46462306a36Sopenharmony_ci return; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci ret = fail_join(group, member, status); 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci deref_member(member); 47062306a36Sopenharmony_ci if (ret) 47162306a36Sopenharmony_ci ib_sa_free_multicast(&member->multicast); 47262306a36Sopenharmony_ci spin_lock_irq(&group->lock); 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci join_state = get_leave_state(group); 47662306a36Sopenharmony_ci if (join_state) { 47762306a36Sopenharmony_ci group->rec.join_state &= ~join_state; 47862306a36Sopenharmony_ci spin_unlock_irq(&group->lock); 47962306a36Sopenharmony_ci if (send_leave(group, join_state)) 48062306a36Sopenharmony_ci goto retest; 48162306a36Sopenharmony_ci } else { 48262306a36Sopenharmony_ci group->state = MCAST_IDLE; 48362306a36Sopenharmony_ci spin_unlock_irq(&group->lock); 48462306a36Sopenharmony_ci release_group(group); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci/* 48962306a36Sopenharmony_ci * Fail a join request if it is still active - at the head of the pending queue. 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_cistatic void process_join_error(struct mcast_group *group, int status) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct mcast_member *member; 49462306a36Sopenharmony_ci int ret; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci spin_lock_irq(&group->lock); 49762306a36Sopenharmony_ci member = list_entry(group->pending_list.next, 49862306a36Sopenharmony_ci struct mcast_member, list); 49962306a36Sopenharmony_ci if (group->last_join == member) { 50062306a36Sopenharmony_ci refcount_inc(&member->refcount); 50162306a36Sopenharmony_ci list_del_init(&member->list); 50262306a36Sopenharmony_ci spin_unlock_irq(&group->lock); 50362306a36Sopenharmony_ci ret = member->multicast.callback(status, &member->multicast); 50462306a36Sopenharmony_ci deref_member(member); 50562306a36Sopenharmony_ci if (ret) 50662306a36Sopenharmony_ci ib_sa_free_multicast(&member->multicast); 50762306a36Sopenharmony_ci } else 50862306a36Sopenharmony_ci spin_unlock_irq(&group->lock); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic void join_handler(int status, struct ib_sa_mcmember_rec *rec, 51262306a36Sopenharmony_ci void *context) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct mcast_group *group = context; 51562306a36Sopenharmony_ci u16 pkey_index = MCAST_INVALID_PKEY_INDEX; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (status) 51862306a36Sopenharmony_ci process_join_error(group, status); 51962306a36Sopenharmony_ci else { 52062306a36Sopenharmony_ci int mgids_changed, is_mgid0; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (ib_find_pkey(group->port->dev->device, 52362306a36Sopenharmony_ci group->port->port_num, be16_to_cpu(rec->pkey), 52462306a36Sopenharmony_ci &pkey_index)) 52562306a36Sopenharmony_ci pkey_index = MCAST_INVALID_PKEY_INDEX; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci spin_lock_irq(&group->port->lock); 52862306a36Sopenharmony_ci if (group->state == MCAST_BUSY && 52962306a36Sopenharmony_ci group->pkey_index == MCAST_INVALID_PKEY_INDEX) 53062306a36Sopenharmony_ci group->pkey_index = pkey_index; 53162306a36Sopenharmony_ci mgids_changed = memcmp(&rec->mgid, &group->rec.mgid, 53262306a36Sopenharmony_ci sizeof(group->rec.mgid)); 53362306a36Sopenharmony_ci group->rec = *rec; 53462306a36Sopenharmony_ci if (mgids_changed) { 53562306a36Sopenharmony_ci rb_erase(&group->node, &group->port->table); 53662306a36Sopenharmony_ci is_mgid0 = !memcmp(&mgid0, &group->rec.mgid, 53762306a36Sopenharmony_ci sizeof(mgid0)); 53862306a36Sopenharmony_ci mcast_insert(group->port, group, is_mgid0); 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci spin_unlock_irq(&group->port->lock); 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci mcast_work_handler(&group->work); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic void leave_handler(int status, struct ib_sa_mcmember_rec *rec, 54662306a36Sopenharmony_ci void *context) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct mcast_group *group = context; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (status && group->retries > 0 && 55162306a36Sopenharmony_ci !send_leave(group, group->leave_state)) 55262306a36Sopenharmony_ci group->retries--; 55362306a36Sopenharmony_ci else 55462306a36Sopenharmony_ci mcast_work_handler(&group->work); 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic struct mcast_group *acquire_group(struct mcast_port *port, 55862306a36Sopenharmony_ci union ib_gid *mgid, gfp_t gfp_mask) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct mcast_group *group, *cur_group; 56162306a36Sopenharmony_ci unsigned long flags; 56262306a36Sopenharmony_ci int is_mgid0; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci is_mgid0 = !memcmp(&mgid0, mgid, sizeof mgid0); 56562306a36Sopenharmony_ci if (!is_mgid0) { 56662306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 56762306a36Sopenharmony_ci group = mcast_find(port, mgid); 56862306a36Sopenharmony_ci if (group) 56962306a36Sopenharmony_ci goto found; 57062306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci group = kzalloc(sizeof *group, gfp_mask); 57462306a36Sopenharmony_ci if (!group) 57562306a36Sopenharmony_ci return NULL; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci group->retries = 3; 57862306a36Sopenharmony_ci group->port = port; 57962306a36Sopenharmony_ci group->rec.mgid = *mgid; 58062306a36Sopenharmony_ci group->pkey_index = MCAST_INVALID_PKEY_INDEX; 58162306a36Sopenharmony_ci INIT_LIST_HEAD(&group->pending_list); 58262306a36Sopenharmony_ci INIT_LIST_HEAD(&group->active_list); 58362306a36Sopenharmony_ci INIT_WORK(&group->work, mcast_work_handler); 58462306a36Sopenharmony_ci spin_lock_init(&group->lock); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 58762306a36Sopenharmony_ci cur_group = mcast_insert(port, group, is_mgid0); 58862306a36Sopenharmony_ci if (cur_group) { 58962306a36Sopenharmony_ci kfree(group); 59062306a36Sopenharmony_ci group = cur_group; 59162306a36Sopenharmony_ci } else 59262306a36Sopenharmony_ci refcount_inc(&port->refcount); 59362306a36Sopenharmony_cifound: 59462306a36Sopenharmony_ci atomic_inc(&group->refcount); 59562306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 59662306a36Sopenharmony_ci return group; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/* 60062306a36Sopenharmony_ci * We serialize all join requests to a single group to make our lives much 60162306a36Sopenharmony_ci * easier. Otherwise, two users could try to join the same group 60262306a36Sopenharmony_ci * simultaneously, with different configurations, one could leave while the 60362306a36Sopenharmony_ci * join is in progress, etc., which makes locking around error recovery 60462306a36Sopenharmony_ci * difficult. 60562306a36Sopenharmony_ci */ 60662306a36Sopenharmony_cistruct ib_sa_multicast * 60762306a36Sopenharmony_ciib_sa_join_multicast(struct ib_sa_client *client, 60862306a36Sopenharmony_ci struct ib_device *device, u32 port_num, 60962306a36Sopenharmony_ci struct ib_sa_mcmember_rec *rec, 61062306a36Sopenharmony_ci ib_sa_comp_mask comp_mask, gfp_t gfp_mask, 61162306a36Sopenharmony_ci int (*callback)(int status, 61262306a36Sopenharmony_ci struct ib_sa_multicast *multicast), 61362306a36Sopenharmony_ci void *context) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct mcast_device *dev; 61662306a36Sopenharmony_ci struct mcast_member *member; 61762306a36Sopenharmony_ci struct ib_sa_multicast *multicast; 61862306a36Sopenharmony_ci int ret; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci dev = ib_get_client_data(device, &mcast_client); 62162306a36Sopenharmony_ci if (!dev) 62262306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci member = kmalloc(sizeof *member, gfp_mask); 62562306a36Sopenharmony_ci if (!member) 62662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci ib_sa_client_get(client); 62962306a36Sopenharmony_ci member->client = client; 63062306a36Sopenharmony_ci member->multicast.rec = *rec; 63162306a36Sopenharmony_ci member->multicast.comp_mask = comp_mask; 63262306a36Sopenharmony_ci member->multicast.callback = callback; 63362306a36Sopenharmony_ci member->multicast.context = context; 63462306a36Sopenharmony_ci init_completion(&member->comp); 63562306a36Sopenharmony_ci refcount_set(&member->refcount, 1); 63662306a36Sopenharmony_ci member->state = MCAST_JOINING; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci member->group = acquire_group(&dev->port[port_num - dev->start_port], 63962306a36Sopenharmony_ci &rec->mgid, gfp_mask); 64062306a36Sopenharmony_ci if (!member->group) { 64162306a36Sopenharmony_ci ret = -ENOMEM; 64262306a36Sopenharmony_ci goto err; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* 64662306a36Sopenharmony_ci * The user will get the multicast structure in their callback. They 64762306a36Sopenharmony_ci * could then free the multicast structure before we can return from 64862306a36Sopenharmony_ci * this routine. So we save the pointer to return before queuing 64962306a36Sopenharmony_ci * any callback. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci multicast = &member->multicast; 65262306a36Sopenharmony_ci queue_join(member); 65362306a36Sopenharmony_ci return multicast; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cierr: 65662306a36Sopenharmony_ci ib_sa_client_put(client); 65762306a36Sopenharmony_ci kfree(member); 65862306a36Sopenharmony_ci return ERR_PTR(ret); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ciEXPORT_SYMBOL(ib_sa_join_multicast); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_civoid ib_sa_free_multicast(struct ib_sa_multicast *multicast) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct mcast_member *member; 66562306a36Sopenharmony_ci struct mcast_group *group; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci member = container_of(multicast, struct mcast_member, multicast); 66862306a36Sopenharmony_ci group = member->group; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci spin_lock_irq(&group->lock); 67162306a36Sopenharmony_ci if (member->state == MCAST_MEMBER) 67262306a36Sopenharmony_ci adjust_membership(group, multicast->rec.join_state, -1); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci list_del_init(&member->list); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (group->state == MCAST_IDLE) { 67762306a36Sopenharmony_ci group->state = MCAST_BUSY; 67862306a36Sopenharmony_ci spin_unlock_irq(&group->lock); 67962306a36Sopenharmony_ci /* Continue to hold reference on group until callback */ 68062306a36Sopenharmony_ci queue_work(mcast_wq, &group->work); 68162306a36Sopenharmony_ci } else { 68262306a36Sopenharmony_ci spin_unlock_irq(&group->lock); 68362306a36Sopenharmony_ci release_group(group); 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci deref_member(member); 68762306a36Sopenharmony_ci wait_for_completion(&member->comp); 68862306a36Sopenharmony_ci ib_sa_client_put(member->client); 68962306a36Sopenharmony_ci kfree(member); 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ciEXPORT_SYMBOL(ib_sa_free_multicast); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ciint ib_sa_get_mcmember_rec(struct ib_device *device, u32 port_num, 69462306a36Sopenharmony_ci union ib_gid *mgid, struct ib_sa_mcmember_rec *rec) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct mcast_device *dev; 69762306a36Sopenharmony_ci struct mcast_port *port; 69862306a36Sopenharmony_ci struct mcast_group *group; 69962306a36Sopenharmony_ci unsigned long flags; 70062306a36Sopenharmony_ci int ret = 0; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci dev = ib_get_client_data(device, &mcast_client); 70362306a36Sopenharmony_ci if (!dev) 70462306a36Sopenharmony_ci return -ENODEV; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci port = &dev->port[port_num - dev->start_port]; 70762306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 70862306a36Sopenharmony_ci group = mcast_find(port, mgid); 70962306a36Sopenharmony_ci if (group) 71062306a36Sopenharmony_ci *rec = group->rec; 71162306a36Sopenharmony_ci else 71262306a36Sopenharmony_ci ret = -EADDRNOTAVAIL; 71362306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci return ret; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ciEXPORT_SYMBOL(ib_sa_get_mcmember_rec); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci/** 72062306a36Sopenharmony_ci * ib_init_ah_from_mcmember - Initialize AH attribute from multicast 72162306a36Sopenharmony_ci * member record and gid of the device. 72262306a36Sopenharmony_ci * @device: RDMA device 72362306a36Sopenharmony_ci * @port_num: Port of the rdma device to consider 72462306a36Sopenharmony_ci * @rec: Multicast member record to use 72562306a36Sopenharmony_ci * @ndev: Optional netdevice, applicable only for RoCE 72662306a36Sopenharmony_ci * @gid_type: GID type to consider 72762306a36Sopenharmony_ci * @ah_attr: AH attribute to fillup on successful completion 72862306a36Sopenharmony_ci * 72962306a36Sopenharmony_ci * ib_init_ah_from_mcmember() initializes AH attribute based on multicast 73062306a36Sopenharmony_ci * member record and other device properties. On success the caller is 73162306a36Sopenharmony_ci * responsible to call rdma_destroy_ah_attr on the ah_attr. Returns 0 on 73262306a36Sopenharmony_ci * success or appropriate error code. 73362306a36Sopenharmony_ci * 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_ciint ib_init_ah_from_mcmember(struct ib_device *device, u32 port_num, 73662306a36Sopenharmony_ci struct ib_sa_mcmember_rec *rec, 73762306a36Sopenharmony_ci struct net_device *ndev, 73862306a36Sopenharmony_ci enum ib_gid_type gid_type, 73962306a36Sopenharmony_ci struct rdma_ah_attr *ah_attr) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci const struct ib_gid_attr *sgid_attr; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci /* GID table is not based on the netdevice for IB link layer, 74462306a36Sopenharmony_ci * so ignore ndev during search. 74562306a36Sopenharmony_ci */ 74662306a36Sopenharmony_ci if (rdma_protocol_ib(device, port_num)) 74762306a36Sopenharmony_ci ndev = NULL; 74862306a36Sopenharmony_ci else if (!rdma_protocol_roce(device, port_num)) 74962306a36Sopenharmony_ci return -EINVAL; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci sgid_attr = rdma_find_gid_by_port(device, &rec->port_gid, 75262306a36Sopenharmony_ci gid_type, port_num, ndev); 75362306a36Sopenharmony_ci if (IS_ERR(sgid_attr)) 75462306a36Sopenharmony_ci return PTR_ERR(sgid_attr); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci memset(ah_attr, 0, sizeof(*ah_attr)); 75762306a36Sopenharmony_ci ah_attr->type = rdma_ah_find_type(device, port_num); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci rdma_ah_set_dlid(ah_attr, be16_to_cpu(rec->mlid)); 76062306a36Sopenharmony_ci rdma_ah_set_sl(ah_attr, rec->sl); 76162306a36Sopenharmony_ci rdma_ah_set_port_num(ah_attr, port_num); 76262306a36Sopenharmony_ci rdma_ah_set_static_rate(ah_attr, rec->rate); 76362306a36Sopenharmony_ci rdma_move_grh_sgid_attr(ah_attr, &rec->mgid, 76462306a36Sopenharmony_ci be32_to_cpu(rec->flow_label), 76562306a36Sopenharmony_ci rec->hop_limit, rec->traffic_class, 76662306a36Sopenharmony_ci sgid_attr); 76762306a36Sopenharmony_ci return 0; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ciEXPORT_SYMBOL(ib_init_ah_from_mcmember); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic void mcast_groups_event(struct mcast_port *port, 77262306a36Sopenharmony_ci enum mcast_group_state state) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct mcast_group *group; 77562306a36Sopenharmony_ci struct rb_node *node; 77662306a36Sopenharmony_ci unsigned long flags; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 77962306a36Sopenharmony_ci for (node = rb_first(&port->table); node; node = rb_next(node)) { 78062306a36Sopenharmony_ci group = rb_entry(node, struct mcast_group, node); 78162306a36Sopenharmony_ci spin_lock(&group->lock); 78262306a36Sopenharmony_ci if (group->state == MCAST_IDLE) { 78362306a36Sopenharmony_ci atomic_inc(&group->refcount); 78462306a36Sopenharmony_ci queue_work(mcast_wq, &group->work); 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci if (group->state != MCAST_GROUP_ERROR) 78762306a36Sopenharmony_ci group->state = state; 78862306a36Sopenharmony_ci spin_unlock(&group->lock); 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic void mcast_event_handler(struct ib_event_handler *handler, 79462306a36Sopenharmony_ci struct ib_event *event) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci struct mcast_device *dev; 79762306a36Sopenharmony_ci int index; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci dev = container_of(handler, struct mcast_device, event_handler); 80062306a36Sopenharmony_ci if (!rdma_cap_ib_mcast(dev->device, event->element.port_num)) 80162306a36Sopenharmony_ci return; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci index = event->element.port_num - dev->start_port; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci switch (event->event) { 80662306a36Sopenharmony_ci case IB_EVENT_PORT_ERR: 80762306a36Sopenharmony_ci case IB_EVENT_LID_CHANGE: 80862306a36Sopenharmony_ci case IB_EVENT_CLIENT_REREGISTER: 80962306a36Sopenharmony_ci mcast_groups_event(&dev->port[index], MCAST_GROUP_ERROR); 81062306a36Sopenharmony_ci break; 81162306a36Sopenharmony_ci case IB_EVENT_PKEY_CHANGE: 81262306a36Sopenharmony_ci mcast_groups_event(&dev->port[index], MCAST_PKEY_EVENT); 81362306a36Sopenharmony_ci break; 81462306a36Sopenharmony_ci default: 81562306a36Sopenharmony_ci break; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic int mcast_add_one(struct ib_device *device) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct mcast_device *dev; 82262306a36Sopenharmony_ci struct mcast_port *port; 82362306a36Sopenharmony_ci int i; 82462306a36Sopenharmony_ci int count = 0; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci dev = kmalloc(struct_size(dev, port, device->phys_port_cnt), 82762306a36Sopenharmony_ci GFP_KERNEL); 82862306a36Sopenharmony_ci if (!dev) 82962306a36Sopenharmony_ci return -ENOMEM; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci dev->start_port = rdma_start_port(device); 83262306a36Sopenharmony_ci dev->end_port = rdma_end_port(device); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci for (i = 0; i <= dev->end_port - dev->start_port; i++) { 83562306a36Sopenharmony_ci if (!rdma_cap_ib_mcast(device, dev->start_port + i)) 83662306a36Sopenharmony_ci continue; 83762306a36Sopenharmony_ci port = &dev->port[i]; 83862306a36Sopenharmony_ci port->dev = dev; 83962306a36Sopenharmony_ci port->port_num = dev->start_port + i; 84062306a36Sopenharmony_ci spin_lock_init(&port->lock); 84162306a36Sopenharmony_ci port->table = RB_ROOT; 84262306a36Sopenharmony_ci init_completion(&port->comp); 84362306a36Sopenharmony_ci refcount_set(&port->refcount, 1); 84462306a36Sopenharmony_ci ++count; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (!count) { 84862306a36Sopenharmony_ci kfree(dev); 84962306a36Sopenharmony_ci return -EOPNOTSUPP; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci dev->device = device; 85362306a36Sopenharmony_ci ib_set_client_data(device, &mcast_client, dev); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci INIT_IB_EVENT_HANDLER(&dev->event_handler, device, mcast_event_handler); 85662306a36Sopenharmony_ci ib_register_event_handler(&dev->event_handler); 85762306a36Sopenharmony_ci return 0; 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic void mcast_remove_one(struct ib_device *device, void *client_data) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct mcast_device *dev = client_data; 86362306a36Sopenharmony_ci struct mcast_port *port; 86462306a36Sopenharmony_ci int i; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci ib_unregister_event_handler(&dev->event_handler); 86762306a36Sopenharmony_ci flush_workqueue(mcast_wq); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci for (i = 0; i <= dev->end_port - dev->start_port; i++) { 87062306a36Sopenharmony_ci if (rdma_cap_ib_mcast(device, dev->start_port + i)) { 87162306a36Sopenharmony_ci port = &dev->port[i]; 87262306a36Sopenharmony_ci deref_port(port); 87362306a36Sopenharmony_ci wait_for_completion(&port->comp); 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci kfree(dev); 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ciint mcast_init(void) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci int ret; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci mcast_wq = alloc_ordered_workqueue("ib_mcast", WQ_MEM_RECLAIM); 88562306a36Sopenharmony_ci if (!mcast_wq) 88662306a36Sopenharmony_ci return -ENOMEM; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci ib_sa_register_client(&sa_client); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci ret = ib_register_client(&mcast_client); 89162306a36Sopenharmony_ci if (ret) 89262306a36Sopenharmony_ci goto err; 89362306a36Sopenharmony_ci return 0; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cierr: 89662306a36Sopenharmony_ci ib_sa_unregister_client(&sa_client); 89762306a36Sopenharmony_ci destroy_workqueue(mcast_wq); 89862306a36Sopenharmony_ci return ret; 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_civoid mcast_cleanup(void) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci ib_unregister_client(&mcast_client); 90462306a36Sopenharmony_ci ib_sa_unregister_client(&sa_client); 90562306a36Sopenharmony_ci destroy_workqueue(mcast_wq); 90662306a36Sopenharmony_ci} 907