multicast.c

来自「linux 内核源代码」· C语言 代码 · 共 837 行 · 第 1/2 页

C
837
字号
/* * Copyright (c) 2006 Intel Corporation.  All rights reserved. * * This software is available to you under a choice of one of two * licenses.  You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * *     Redistribution and use in source and binary forms, with or *     without modification, are permitted provided that the following *     conditions are met: * *      - Redistributions of source code must retain the above *        copyright notice, this list of conditions and the following *        disclaimer. * *      - Redistributions in binary form must reproduce the above *        copyright notice, this list of conditions and the following *        disclaimer in the documentation and/or other materials *        provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */#include <linux/completion.h>#include <linux/dma-mapping.h>#include <linux/err.h>#include <linux/interrupt.h>#include <linux/bitops.h>#include <linux/random.h>#include <rdma/ib_cache.h>#include "sa.h"static void mcast_add_one(struct ib_device *device);static void mcast_remove_one(struct ib_device *device);static struct ib_client mcast_client = {	.name   = "ib_multicast",	.add    = mcast_add_one,	.remove = mcast_remove_one};static struct ib_sa_client	sa_client;static struct workqueue_struct	*mcast_wq;static union ib_gid mgid0;struct mcast_device;struct mcast_port {	struct mcast_device	*dev;	spinlock_t		lock;	struct rb_root		table;	atomic_t		refcount;	struct completion	comp;	u8			port_num;};struct mcast_device {	struct ib_device	*device;	struct ib_event_handler	event_handler;	int			start_port;	int			end_port;	struct mcast_port	port[0];};enum mcast_state {	MCAST_IDLE,	MCAST_JOINING,	MCAST_MEMBER,	MCAST_BUSY,	MCAST_ERROR};struct mcast_member;struct mcast_group {	struct ib_sa_mcmember_rec rec;	struct rb_node		node;	struct mcast_port	*port;	spinlock_t		lock;	struct work_struct	work;	struct list_head	pending_list;	struct list_head	active_list;	struct mcast_member	*last_join;	int			members[3];	atomic_t		refcount;	enum mcast_state	state;	struct ib_sa_query	*query;	int			query_id;};struct mcast_member {	struct ib_sa_multicast	multicast;	struct ib_sa_client	*client;	struct mcast_group	*group;	struct list_head	list;	enum mcast_state	state;	atomic_t		refcount;	struct completion	comp;};static void join_handler(int status, struct ib_sa_mcmember_rec *rec,			 void *context);static void leave_handler(int status, struct ib_sa_mcmember_rec *rec,			  void *context);static struct mcast_group *mcast_find(struct mcast_port *port,				      union ib_gid *mgid){	struct rb_node *node = port->table.rb_node;	struct mcast_group *group;	int ret;	while (node) {		group = rb_entry(node, struct mcast_group, node);		ret = memcmp(mgid->raw, group->rec.mgid.raw, sizeof *mgid);		if (!ret)			return group;		if (ret < 0)			node = node->rb_left;		else			node = node->rb_right;	}	return NULL;}static struct mcast_group *mcast_insert(struct mcast_port *port,					struct mcast_group *group,					int allow_duplicates){	struct rb_node **link = &port->table.rb_node;	struct rb_node *parent = NULL;	struct mcast_group *cur_group;	int ret;	while (*link) {		parent = *link;		cur_group = rb_entry(parent, struct mcast_group, node);		ret = memcmp(group->rec.mgid.raw, cur_group->rec.mgid.raw,			     sizeof group->rec.mgid);		if (ret < 0)			link = &(*link)->rb_left;		else if (ret > 0)			link = &(*link)->rb_right;		else if (allow_duplicates)			link = &(*link)->rb_left;		else			return cur_group;	}	rb_link_node(&group->node, parent, link);	rb_insert_color(&group->node, &port->table);	return NULL;}static void deref_port(struct mcast_port *port){	if (atomic_dec_and_test(&port->refcount))		complete(&port->comp);}static void release_group(struct mcast_group *group){	struct mcast_port *port = group->port;	unsigned long flags;	spin_lock_irqsave(&port->lock, flags);	if (atomic_dec_and_test(&group->refcount)) {		rb_erase(&group->node, &port->table);		spin_unlock_irqrestore(&port->lock, flags);		kfree(group);		deref_port(port);	} else		spin_unlock_irqrestore(&port->lock, flags);}static void deref_member(struct mcast_member *member){	if (atomic_dec_and_test(&member->refcount))		complete(&member->comp);}static void queue_join(struct mcast_member *member){	struct mcast_group *group = member->group;	unsigned long flags;	spin_lock_irqsave(&group->lock, flags);	list_add_tail(&member->list, &group->pending_list);	if (group->state == MCAST_IDLE) {		group->state = MCAST_BUSY;		atomic_inc(&group->refcount);		queue_work(mcast_wq, &group->work);	}	spin_unlock_irqrestore(&group->lock, flags);}/* * A multicast group has three types of members: full member, non member, and * send only member.  We need to keep track of the number of members of each * type based on their join state.  Adjust the number of members the belong to * the specified join states. */static void adjust_membership(struct mcast_group *group, u8 join_state, int inc){	int i;	for (i = 0; i < 3; i++, join_state >>= 1)		if (join_state & 0x1)			group->members[i] += inc;}/* * If a multicast group has zero members left for a particular join state, but * the group is still a member with the SA, we need to leave that join state. * Determine which join states we still belong to, but that do not have any * active members. */static u8 get_leave_state(struct mcast_group *group){	u8 leave_state = 0;	int i;	for (i = 0; i < 3; i++)		if (!group->members[i])			leave_state |= (0x1 << i);	return leave_state & group->rec.join_state;}static int check_selector(ib_sa_comp_mask comp_mask,			  ib_sa_comp_mask selector_mask,			  ib_sa_comp_mask value_mask,			  u8 selector, u8 src_value, u8 dst_value){	int err;	if (!(comp_mask & selector_mask) || !(comp_mask & value_mask))		return 0;	switch (selector) {	case IB_SA_GT:		err = (src_value <= dst_value);		break;	case IB_SA_LT:		err = (src_value >= dst_value);		break;	case IB_SA_EQ:		err = (src_value != dst_value);		break;	default:		err = 0;		break;	}	return err;}static int cmp_rec(struct ib_sa_mcmember_rec *src,		   struct ib_sa_mcmember_rec *dst, ib_sa_comp_mask comp_mask){	/* MGID must already match */	if (comp_mask & IB_SA_MCMEMBER_REC_PORT_GID &&	    memcmp(&src->port_gid, &dst->port_gid, sizeof src->port_gid))		return -EINVAL;	if (comp_mask & IB_SA_MCMEMBER_REC_QKEY && src->qkey != dst->qkey)		return -EINVAL;	if (comp_mask & IB_SA_MCMEMBER_REC_MLID && src->mlid != dst->mlid)		return -EINVAL;	if (check_selector(comp_mask, IB_SA_MCMEMBER_REC_MTU_SELECTOR,			   IB_SA_MCMEMBER_REC_MTU, dst->mtu_selector,			   src->mtu, dst->mtu))		return -EINVAL;	if (comp_mask & IB_SA_MCMEMBER_REC_TRAFFIC_CLASS &&	    src->traffic_class != dst->traffic_class)		return -EINVAL;	if (comp_mask & IB_SA_MCMEMBER_REC_PKEY && src->pkey != dst->pkey)		return -EINVAL;	if (check_selector(comp_mask, IB_SA_MCMEMBER_REC_RATE_SELECTOR,			   IB_SA_MCMEMBER_REC_RATE, dst->rate_selector,			   src->rate, dst->rate))		return -EINVAL;	if (check_selector(comp_mask,			   IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME_SELECTOR,			   IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME,			   dst->packet_life_time_selector,			   src->packet_life_time, dst->packet_life_time))		return -EINVAL;	if (comp_mask & IB_SA_MCMEMBER_REC_SL && src->sl != dst->sl)		return -EINVAL;	if (comp_mask & IB_SA_MCMEMBER_REC_FLOW_LABEL &&	    src->flow_label != dst->flow_label)		return -EINVAL;	if (comp_mask & IB_SA_MCMEMBER_REC_HOP_LIMIT &&	    src->hop_limit != dst->hop_limit)		return -EINVAL;	if (comp_mask & IB_SA_MCMEMBER_REC_SCOPE && src->scope != dst->scope)		return -EINVAL;	/* join_state checked separately, proxy_join ignored */	return 0;}static int send_join(struct mcast_group *group, struct mcast_member *member){	struct mcast_port *port = group->port;	int ret;	group->last_join = member;	ret = ib_sa_mcmember_rec_query(&sa_client, port->dev->device,				       port->port_num, IB_MGMT_METHOD_SET,				       &member->multicast.rec,				       member->multicast.comp_mask,				       3000, GFP_KERNEL, join_handler, group,				       &group->query);	if (ret >= 0) {		group->query_id = ret;		ret = 0;	}	return ret;}static int send_leave(struct mcast_group *group, u8 leave_state){	struct mcast_port *port = group->port;	struct ib_sa_mcmember_rec rec;	int ret;	rec = group->rec;	rec.join_state = leave_state;	ret = ib_sa_mcmember_rec_query(&sa_client, port->dev->device,				       port->port_num, IB_SA_METHOD_DELETE, &rec,				       IB_SA_MCMEMBER_REC_MGID     |				       IB_SA_MCMEMBER_REC_PORT_GID |				       IB_SA_MCMEMBER_REC_JOIN_STATE,				       3000, GFP_KERNEL, leave_handler,				       group, &group->query);	if (ret >= 0) {		group->query_id = ret;		ret = 0;	}	return ret;}static void join_group(struct mcast_group *group, struct mcast_member *member,		       u8 join_state){	member->state = MCAST_MEMBER;	adjust_membership(group, join_state, 1);	group->rec.join_state |= join_state;	member->multicast.rec = group->rec;	member->multicast.rec.join_state = join_state;	list_move(&member->list, &group->active_list);}static int fail_join(struct mcast_group *group, struct mcast_member *member,		     int status){	spin_lock_irq(&group->lock);	list_del_init(&member->list);	spin_unlock_irq(&group->lock);	return member->multicast.callback(status, &member->multicast);}static void process_group_error(struct mcast_group *group){	struct mcast_member *member;	int ret;	spin_lock_irq(&group->lock);	while (!list_empty(&group->active_list)) {		member = list_entry(group->active_list.next,				    struct mcast_member, list);		atomic_inc(&member->refcount);		list_del_init(&member->list);		adjust_membership(group, member->multicast.rec.join_state, -1);		member->state = MCAST_ERROR;		spin_unlock_irq(&group->lock);		ret = member->multicast.callback(-ENETRESET,						 &member->multicast);		deref_member(member);		if (ret)			ib_sa_free_multicast(&member->multicast);		spin_lock_irq(&group->lock);	}	group->rec.join_state = 0;	group->state = MCAST_BUSY;	spin_unlock_irq(&group->lock);}static void mcast_work_handler(struct work_struct *work){	struct mcast_group *group;	struct mcast_member *member;	struct ib_sa_multicast *multicast;	int status, ret;	u8 join_state;	group = container_of(work, typeof(*group), work);retest:	spin_lock_irq(&group->lock);	while (!list_empty(&group->pending_list) ||	       (group->state == MCAST_ERROR)) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?