brintos

brintos / linux-shallow public Read only

0
0
Text · 8.6 KiB · 59045bd Raw
402 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright(c) 2016 Intel Corporation.4 */5 6#include <linux/slab.h>7#include <linux/sched.h>8#include <linux/rculist.h>9#include <rdma/rdma_vt.h>10#include <rdma/rdmavt_qp.h>11 12#include "mcast.h"13 14/**15 * rvt_driver_mcast_init - init resources for multicast16 * @rdi: rvt dev struct17 *18 * This is per device that registers with rdmavt19 */20void rvt_driver_mcast_init(struct rvt_dev_info *rdi)21{22	/*23	 * Anything that needs setup for multicast on a per driver or per rdi24	 * basis should be done in here.25	 */26	spin_lock_init(&rdi->n_mcast_grps_lock);27}28 29/**30 * rvt_mcast_qp_alloc - alloc a struct to link a QP to mcast GID struct31 * @qp: the QP to link32 */33static struct rvt_mcast_qp *rvt_mcast_qp_alloc(struct rvt_qp *qp)34{35	struct rvt_mcast_qp *mqp;36 37	mqp = kmalloc(sizeof(*mqp), GFP_KERNEL);38	if (!mqp)39		goto bail;40 41	mqp->qp = qp;42	rvt_get_qp(qp);43 44bail:45	return mqp;46}47 48static void rvt_mcast_qp_free(struct rvt_mcast_qp *mqp)49{50	struct rvt_qp *qp = mqp->qp;51 52	/* Notify hfi1_destroy_qp() if it is waiting. */53	rvt_put_qp(qp);54 55	kfree(mqp);56}57 58/**59 * rvt_mcast_alloc - allocate the multicast GID structure60 * @mgid: the multicast GID61 * @lid: the muilticast LID (host order)62 *63 * A list of QPs will be attached to this structure.64 */65static struct rvt_mcast *rvt_mcast_alloc(union ib_gid *mgid, u16 lid)66{67	struct rvt_mcast *mcast;68 69	mcast = kzalloc(sizeof(*mcast), GFP_KERNEL);70	if (!mcast)71		goto bail;72 73	mcast->mcast_addr.mgid = *mgid;74	mcast->mcast_addr.lid = lid;75 76	INIT_LIST_HEAD(&mcast->qp_list);77	init_waitqueue_head(&mcast->wait);78	atomic_set(&mcast->refcount, 0);79 80bail:81	return mcast;82}83 84static void rvt_mcast_free(struct rvt_mcast *mcast)85{86	struct rvt_mcast_qp *p, *tmp;87 88	list_for_each_entry_safe(p, tmp, &mcast->qp_list, list)89		rvt_mcast_qp_free(p);90 91	kfree(mcast);92}93 94/**95 * rvt_mcast_find - search the global table for the given multicast GID/LID96 * NOTE: It is valid to have 1 MLID with multiple MGIDs.  It is not valid97 * to have 1 MGID with multiple MLIDs.98 * @ibp: the IB port structure99 * @mgid: the multicast GID to search for100 * @lid: the multicast LID portion of the multicast address (host order)101 *102 * The caller is responsible for decrementing the reference count if found.103 *104 * Return: NULL if not found.105 */106struct rvt_mcast *rvt_mcast_find(struct rvt_ibport *ibp, union ib_gid *mgid,107				 u16 lid)108{109	struct rb_node *n;110	unsigned long flags;111	struct rvt_mcast *found = NULL;112 113	spin_lock_irqsave(&ibp->lock, flags);114	n = ibp->mcast_tree.rb_node;115	while (n) {116		int ret;117		struct rvt_mcast *mcast;118 119		mcast = rb_entry(n, struct rvt_mcast, rb_node);120 121		ret = memcmp(mgid->raw, mcast->mcast_addr.mgid.raw,122			     sizeof(*mgid));123		if (ret < 0) {124			n = n->rb_left;125		} else if (ret > 0) {126			n = n->rb_right;127		} else {128			/* MGID/MLID must match */129			if (mcast->mcast_addr.lid == lid) {130				atomic_inc(&mcast->refcount);131				found = mcast;132			}133			break;134		}135	}136	spin_unlock_irqrestore(&ibp->lock, flags);137	return found;138}139EXPORT_SYMBOL(rvt_mcast_find);140 141/*142 * rvt_mcast_add - insert mcast GID into table and attach QP struct143 * @mcast: the mcast GID table144 * @mqp: the QP to attach145 *146 * Return: zero if both were added.  Return EEXIST if the GID was already in147 * the table but the QP was added.  Return ESRCH if the QP was already148 * attached and neither structure was added. Return EINVAL if the MGID was149 * found, but the MLID did NOT match.150 */151static int rvt_mcast_add(struct rvt_dev_info *rdi, struct rvt_ibport *ibp,152			 struct rvt_mcast *mcast, struct rvt_mcast_qp *mqp)153{154	struct rb_node **n = &ibp->mcast_tree.rb_node;155	struct rb_node *pn = NULL;156	int ret;157 158	spin_lock_irq(&ibp->lock);159 160	while (*n) {161		struct rvt_mcast *tmcast;162		struct rvt_mcast_qp *p;163 164		pn = *n;165		tmcast = rb_entry(pn, struct rvt_mcast, rb_node);166 167		ret = memcmp(mcast->mcast_addr.mgid.raw,168			     tmcast->mcast_addr.mgid.raw,169			     sizeof(mcast->mcast_addr.mgid));170		if (ret < 0) {171			n = &pn->rb_left;172			continue;173		}174		if (ret > 0) {175			n = &pn->rb_right;176			continue;177		}178 179		if (tmcast->mcast_addr.lid != mcast->mcast_addr.lid) {180			ret = EINVAL;181			goto bail;182		}183 184		/* Search the QP list to see if this is already there. */185		list_for_each_entry_rcu(p, &tmcast->qp_list, list) {186			if (p->qp == mqp->qp) {187				ret = ESRCH;188				goto bail;189			}190		}191		if (tmcast->n_attached ==192		    rdi->dparms.props.max_mcast_qp_attach) {193			ret = ENOMEM;194			goto bail;195		}196 197		tmcast->n_attached++;198 199		list_add_tail_rcu(&mqp->list, &tmcast->qp_list);200		ret = EEXIST;201		goto bail;202	}203 204	spin_lock(&rdi->n_mcast_grps_lock);205	if (rdi->n_mcast_grps_allocated == rdi->dparms.props.max_mcast_grp) {206		spin_unlock(&rdi->n_mcast_grps_lock);207		ret = ENOMEM;208		goto bail;209	}210 211	rdi->n_mcast_grps_allocated++;212	spin_unlock(&rdi->n_mcast_grps_lock);213 214	mcast->n_attached++;215 216	list_add_tail_rcu(&mqp->list, &mcast->qp_list);217 218	atomic_inc(&mcast->refcount);219	rb_link_node(&mcast->rb_node, pn, n);220	rb_insert_color(&mcast->rb_node, &ibp->mcast_tree);221 222	ret = 0;223 224bail:225	spin_unlock_irq(&ibp->lock);226 227	return ret;228}229 230/**231 * rvt_attach_mcast - attach a qp to a multicast group232 * @ibqp: Infiniband qp233 * @gid: multicast guid234 * @lid: multicast lid235 *236 * Return: 0 on success237 */238int rvt_attach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)239{240	struct rvt_qp *qp = ibqp_to_rvtqp(ibqp);241	struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device);242	struct rvt_ibport *ibp = rdi->ports[qp->port_num - 1];243	struct rvt_mcast *mcast;244	struct rvt_mcast_qp *mqp;245	int ret = -ENOMEM;246 247	if (ibqp->qp_num <= 1 || qp->state == IB_QPS_RESET)248		return -EINVAL;249 250	/*251	 * Allocate data structures since its better to do this outside of252	 * spin locks and it will most likely be needed.253	 */254	mcast = rvt_mcast_alloc(gid, lid);255	if (!mcast)256		return -ENOMEM;257 258	mqp = rvt_mcast_qp_alloc(qp);259	if (!mqp)260		goto bail_mcast;261 262	switch (rvt_mcast_add(rdi, ibp, mcast, mqp)) {263	case ESRCH:264		/* Neither was used: OK to attach the same QP twice. */265		ret = 0;266		goto bail_mqp;267	case EEXIST: /* The mcast wasn't used */268		ret = 0;269		goto bail_mcast;270	case ENOMEM:271		/* Exceeded the maximum number of mcast groups. */272		ret = -ENOMEM;273		goto bail_mqp;274	case EINVAL:275		/* Invalid MGID/MLID pair */276		ret = -EINVAL;277		goto bail_mqp;278	default:279		break;280	}281 282	return 0;283 284bail_mqp:285	rvt_mcast_qp_free(mqp);286 287bail_mcast:288	rvt_mcast_free(mcast);289 290	return ret;291}292 293/**294 * rvt_detach_mcast - remove a qp from a multicast group295 * @ibqp: Infiniband qp296 * @gid: multicast guid297 * @lid: multicast lid298 *299 * Return: 0 on success300 */301int rvt_detach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)302{303	struct rvt_qp *qp = ibqp_to_rvtqp(ibqp);304	struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device);305	struct rvt_ibport *ibp = rdi->ports[qp->port_num - 1];306	struct rvt_mcast *mcast = NULL;307	struct rvt_mcast_qp *p, *tmp, *delp = NULL;308	struct rb_node *n;309	int last = 0;310	int ret = 0;311 312	if (ibqp->qp_num <= 1)313		return -EINVAL;314 315	spin_lock_irq(&ibp->lock);316 317	/* Find the GID in the mcast table. */318	n = ibp->mcast_tree.rb_node;319	while (1) {320		if (!n) {321			spin_unlock_irq(&ibp->lock);322			return -EINVAL;323		}324 325		mcast = rb_entry(n, struct rvt_mcast, rb_node);326		ret = memcmp(gid->raw, mcast->mcast_addr.mgid.raw,327			     sizeof(*gid));328		if (ret < 0) {329			n = n->rb_left;330		} else if (ret > 0) {331			n = n->rb_right;332		} else {333			/* MGID/MLID must match */334			if (mcast->mcast_addr.lid != lid) {335				spin_unlock_irq(&ibp->lock);336				return -EINVAL;337			}338			break;339		}340	}341 342	/* Search the QP list. */343	list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) {344		if (p->qp != qp)345			continue;346		/*347		 * We found it, so remove it, but don't poison the forward348		 * link until we are sure there are no list walkers.349		 */350		list_del_rcu(&p->list);351		mcast->n_attached--;352		delp = p;353 354		/* If this was the last attached QP, remove the GID too. */355		if (list_empty(&mcast->qp_list)) {356			rb_erase(&mcast->rb_node, &ibp->mcast_tree);357			last = 1;358		}359		break;360	}361 362	spin_unlock_irq(&ibp->lock);363	/* QP not attached */364	if (!delp)365		return -EINVAL;366 367	/*368	 * Wait for any list walkers to finish before freeing the369	 * list element.370	 */371	wait_event(mcast->wait, atomic_read(&mcast->refcount) <= 1);372	rvt_mcast_qp_free(delp);373 374	if (last) {375		atomic_dec(&mcast->refcount);376		wait_event(mcast->wait, !atomic_read(&mcast->refcount));377		rvt_mcast_free(mcast);378		spin_lock_irq(&rdi->n_mcast_grps_lock);379		rdi->n_mcast_grps_allocated--;380		spin_unlock_irq(&rdi->n_mcast_grps_lock);381	}382 383	return 0;384}385 386/**387 * rvt_mcast_tree_empty - determine if any qps are attached to any mcast group388 * @rdi: rvt dev struct389 *390 * Return: in use count391 */392int rvt_mcast_tree_empty(struct rvt_dev_info *rdi)393{394	int i;395	int in_use = 0;396 397	for (i = 0; i < rdi->dparms.nports; i++)398		if (rdi->ports[i]->mcast_tree.rb_node)399			in_use++;400	return in_use;401}402