multicast.c

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

C
837
字号
		if (group->state == MCAST_ERROR) {			spin_unlock_irq(&group->lock);			process_group_error(group);			goto retest;		}		member = list_entry(group->pending_list.next,				    struct mcast_member, list);		multicast = &member->multicast;		join_state = multicast->rec.join_state;		atomic_inc(&member->refcount);		if (join_state == (group->rec.join_state & join_state)) {			status = cmp_rec(&group->rec, &multicast->rec,					 multicast->comp_mask);			if (!status)				join_group(group, member, join_state);			else				list_del_init(&member->list);			spin_unlock_irq(&group->lock);			ret = multicast->callback(status, multicast);		} else {			spin_unlock_irq(&group->lock);			status = send_join(group, member);			if (!status) {				deref_member(member);				return;			}			ret = fail_join(group, member, status);		}		deref_member(member);		if (ret)			ib_sa_free_multicast(&member->multicast);		spin_lock_irq(&group->lock);	}	join_state = get_leave_state(group);	if (join_state) {		group->rec.join_state &= ~join_state;		spin_unlock_irq(&group->lock);		if (send_leave(group, join_state))			goto retest;	} else {		group->state = MCAST_IDLE;		spin_unlock_irq(&group->lock);		release_group(group);	}}/* * Fail a join request if it is still active - at the head of the pending queue. */static void process_join_error(struct mcast_group *group, int status){	struct mcast_member *member;	int ret;	spin_lock_irq(&group->lock);	member = list_entry(group->pending_list.next,			    struct mcast_member, list);	if (group->last_join == member) {		atomic_inc(&member->refcount);		list_del_init(&member->list);		spin_unlock_irq(&group->lock);		ret = member->multicast.callback(status, &member->multicast);		deref_member(member);		if (ret)			ib_sa_free_multicast(&member->multicast);	} else		spin_unlock_irq(&group->lock);}static void join_handler(int status, struct ib_sa_mcmember_rec *rec,			 void *context){	struct mcast_group *group = context;	if (status)		process_join_error(group, status);	else {		spin_lock_irq(&group->port->lock);		group->rec = *rec;		if (!memcmp(&mgid0, &group->rec.mgid, sizeof mgid0)) {			rb_erase(&group->node, &group->port->table);			mcast_insert(group->port, group, 1);		}		spin_unlock_irq(&group->port->lock);	}	mcast_work_handler(&group->work);}static void leave_handler(int status, struct ib_sa_mcmember_rec *rec,			  void *context){	struct mcast_group *group = context;	mcast_work_handler(&group->work);}static struct mcast_group *acquire_group(struct mcast_port *port,					 union ib_gid *mgid, gfp_t gfp_mask){	struct mcast_group *group, *cur_group;	unsigned long flags;	int is_mgid0;	is_mgid0 = !memcmp(&mgid0, mgid, sizeof mgid0);	if (!is_mgid0) {		spin_lock_irqsave(&port->lock, flags);		group = mcast_find(port, mgid);		if (group)			goto found;		spin_unlock_irqrestore(&port->lock, flags);	}	group = kzalloc(sizeof *group, gfp_mask);	if (!group)		return NULL;	group->port = port;	group->rec.mgid = *mgid;	INIT_LIST_HEAD(&group->pending_list);	INIT_LIST_HEAD(&group->active_list);	INIT_WORK(&group->work, mcast_work_handler);	spin_lock_init(&group->lock);	spin_lock_irqsave(&port->lock, flags);	cur_group = mcast_insert(port, group, is_mgid0);	if (cur_group) {		kfree(group);		group = cur_group;	} else		atomic_inc(&port->refcount);found:	atomic_inc(&group->refcount);	spin_unlock_irqrestore(&port->lock, flags);	return group;}/* * We serialize all join requests to a single group to make our lives much * easier.  Otherwise, two users could try to join the same group * simultaneously, with different configurations, one could leave while the * join is in progress, etc., which makes locking around error recovery * difficult. */struct ib_sa_multicast *ib_sa_join_multicast(struct ib_sa_client *client,		     struct ib_device *device, u8 port_num,		     struct ib_sa_mcmember_rec *rec,		     ib_sa_comp_mask comp_mask, gfp_t gfp_mask,		     int (*callback)(int status,				     struct ib_sa_multicast *multicast),		     void *context){	struct mcast_device *dev;	struct mcast_member *member;	struct ib_sa_multicast *multicast;	int ret;	dev = ib_get_client_data(device, &mcast_client);	if (!dev)		return ERR_PTR(-ENODEV);	member = kmalloc(sizeof *member, gfp_mask);	if (!member)		return ERR_PTR(-ENOMEM);	ib_sa_client_get(client);	member->client = client;	member->multicast.rec = *rec;	member->multicast.comp_mask = comp_mask;	member->multicast.callback = callback;	member->multicast.context = context;	init_completion(&member->comp);	atomic_set(&member->refcount, 1);	member->state = MCAST_JOINING;	member->group = acquire_group(&dev->port[port_num - dev->start_port],				      &rec->mgid, gfp_mask);	if (!member->group) {		ret = -ENOMEM;		goto err;	}	/*	 * The user will get the multicast structure in their callback.  They	 * could then free the multicast structure before we can return from	 * this routine.  So we save the pointer to return before queuing	 * any callback.	 */	multicast = &member->multicast;	queue_join(member);	return multicast;err:	ib_sa_client_put(client);	kfree(member);	return ERR_PTR(ret);}EXPORT_SYMBOL(ib_sa_join_multicast);void ib_sa_free_multicast(struct ib_sa_multicast *multicast){	struct mcast_member *member;	struct mcast_group *group;	member = container_of(multicast, struct mcast_member, multicast);	group = member->group;	spin_lock_irq(&group->lock);	if (member->state == MCAST_MEMBER)		adjust_membership(group, multicast->rec.join_state, -1);	list_del_init(&member->list);	if (group->state == MCAST_IDLE) {		group->state = MCAST_BUSY;		spin_unlock_irq(&group->lock);		/* Continue to hold reference on group until callback */		queue_work(mcast_wq, &group->work);	} else {		spin_unlock_irq(&group->lock);		release_group(group);	}	deref_member(member);	wait_for_completion(&member->comp);	ib_sa_client_put(member->client);	kfree(member);}EXPORT_SYMBOL(ib_sa_free_multicast);int ib_sa_get_mcmember_rec(struct ib_device *device, u8 port_num,			   union ib_gid *mgid, struct ib_sa_mcmember_rec *rec){	struct mcast_device *dev;	struct mcast_port *port;	struct mcast_group *group;	unsigned long flags;	int ret = 0;	dev = ib_get_client_data(device, &mcast_client);	if (!dev)		return -ENODEV;	port = &dev->port[port_num - dev->start_port];	spin_lock_irqsave(&port->lock, flags);	group = mcast_find(port, mgid);	if (group)		*rec = group->rec;	else		ret = -EADDRNOTAVAIL;	spin_unlock_irqrestore(&port->lock, flags);	return ret;}EXPORT_SYMBOL(ib_sa_get_mcmember_rec);int ib_init_ah_from_mcmember(struct ib_device *device, u8 port_num,			     struct ib_sa_mcmember_rec *rec,			     struct ib_ah_attr *ah_attr){	int ret;	u16 gid_index;	u8 p;	ret = ib_find_cached_gid(device, &rec->port_gid, &p, &gid_index);	if (ret)		return ret;	memset(ah_attr, 0, sizeof *ah_attr);	ah_attr->dlid = be16_to_cpu(rec->mlid);	ah_attr->sl = rec->sl;	ah_attr->port_num = port_num;	ah_attr->static_rate = rec->rate;	ah_attr->ah_flags = IB_AH_GRH;	ah_attr->grh.dgid = rec->mgid;	ah_attr->grh.sgid_index = (u8) gid_index;	ah_attr->grh.flow_label = be32_to_cpu(rec->flow_label);	ah_attr->grh.hop_limit = rec->hop_limit;	ah_attr->grh.traffic_class = rec->traffic_class;	return 0;}EXPORT_SYMBOL(ib_init_ah_from_mcmember);static void mcast_groups_lost(struct mcast_port *port){	struct mcast_group *group;	struct rb_node *node;	unsigned long flags;	spin_lock_irqsave(&port->lock, flags);	for (node = rb_first(&port->table); node; node = rb_next(node)) {		group = rb_entry(node, struct mcast_group, node);		spin_lock(&group->lock);		if (group->state == MCAST_IDLE) {			atomic_inc(&group->refcount);			queue_work(mcast_wq, &group->work);		}		group->state = MCAST_ERROR;		spin_unlock(&group->lock);	}	spin_unlock_irqrestore(&port->lock, flags);}static void mcast_event_handler(struct ib_event_handler *handler,				struct ib_event *event){	struct mcast_device *dev;	dev = container_of(handler, struct mcast_device, event_handler);	switch (event->event) {	case IB_EVENT_PORT_ERR:	case IB_EVENT_LID_CHANGE:	case IB_EVENT_SM_CHANGE:	case IB_EVENT_CLIENT_REREGISTER:		mcast_groups_lost(&dev->port[event->element.port_num -					     dev->start_port]);		break;	default:		break;	}}static void mcast_add_one(struct ib_device *device){	struct mcast_device *dev;	struct mcast_port *port;	int i;	if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)		return;	dev = kmalloc(sizeof *dev + device->phys_port_cnt * sizeof *port,		      GFP_KERNEL);	if (!dev)		return;	if (device->node_type == RDMA_NODE_IB_SWITCH)		dev->start_port = dev->end_port = 0;	else {		dev->start_port = 1;		dev->end_port = device->phys_port_cnt;	}	for (i = 0; i <= dev->end_port - dev->start_port; i++) {		port = &dev->port[i];		port->dev = dev;		port->port_num = dev->start_port + i;		spin_lock_init(&port->lock);		port->table = RB_ROOT;		init_completion(&port->comp);		atomic_set(&port->refcount, 1);	}	dev->device = device;	ib_set_client_data(device, &mcast_client, dev);	INIT_IB_EVENT_HANDLER(&dev->event_handler, device, mcast_event_handler);	ib_register_event_handler(&dev->event_handler);}static void mcast_remove_one(struct ib_device *device){	struct mcast_device *dev;	struct mcast_port *port;	int i;	dev = ib_get_client_data(device, &mcast_client);	if (!dev)		return;	ib_unregister_event_handler(&dev->event_handler);	flush_workqueue(mcast_wq);	for (i = 0; i <= dev->end_port - dev->start_port; i++) {		port = &dev->port[i];		deref_port(port);		wait_for_completion(&port->comp);	}	kfree(dev);}int mcast_init(void){	int ret;	mcast_wq = create_singlethread_workqueue("ib_mcast");	if (!mcast_wq)		return -ENOMEM;	ib_sa_register_client(&sa_client);	ret = ib_register_client(&mcast_client);	if (ret)		goto err;	return 0;err:	ib_sa_unregister_client(&sa_client);	destroy_workqueue(mcast_wq);	return ret;}void mcast_cleanup(void){	ib_unregister_client(&mcast_client);	ib_sa_unregister_client(&sa_client);	destroy_workqueue(mcast_wq);}

⌨️ 快捷键说明

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