Path: blob/master/drivers/infiniband/hw/mthca/mthca_mcg.c
15112 views
/*1* Copyright (c) 2004 Topspin Communications. All rights reserved.2*3* This software is available to you under a choice of one of two4* licenses. You may choose to be licensed under the terms of the GNU5* General Public License (GPL) Version 2, available from the file6* COPYING in the main directory of this source tree, or the7* OpenIB.org BSD license below:8*9* Redistribution and use in source and binary forms, with or10* without modification, are permitted provided that the following11* conditions are met:12*13* - Redistributions of source code must retain the above14* copyright notice, this list of conditions and the following15* disclaimer.16*17* - Redistributions in binary form must reproduce the above18* copyright notice, this list of conditions and the following19* disclaimer in the documentation and/or other materials20* provided with the distribution.21*22* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,23* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF24* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND25* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS26* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN27* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN28* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE29* SOFTWARE.30*/3132#include <linux/string.h>33#include <linux/gfp.h>3435#include "mthca_dev.h"36#include "mthca_cmd.h"3738struct mthca_mgm {39__be32 next_gid_index;40u32 reserved[3];41u8 gid[16];42__be32 qp[MTHCA_QP_PER_MGM];43};4445static const u8 zero_gid[16]; /* automatically initialized to 0 */4647/*48* Caller must hold MCG table semaphore. gid and mgm parameters must49* be properly aligned for command interface.50*51* Returns 0 unless a firmware command error occurs.52*53* If GID is found in MGM or MGM is empty, *index = *hash, *prev = -154* and *mgm holds MGM entry.55*56* if GID is found in AMGM, *index = index in AMGM, *prev = index of57* previous entry in hash chain and *mgm holds AMGM entry.58*59* If no AMGM exists for given gid, *index = -1, *prev = index of last60* entry in hash chain and *mgm holds end of hash chain.61*/62static int find_mgm(struct mthca_dev *dev,63u8 *gid, struct mthca_mailbox *mgm_mailbox,64u16 *hash, int *prev, int *index)65{66struct mthca_mailbox *mailbox;67struct mthca_mgm *mgm = mgm_mailbox->buf;68u8 *mgid;69int err;70u8 status;7172mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);73if (IS_ERR(mailbox))74return -ENOMEM;75mgid = mailbox->buf;7677memcpy(mgid, gid, 16);7879err = mthca_MGID_HASH(dev, mailbox, hash, &status);80if (err)81goto out;82if (status) {83mthca_err(dev, "MGID_HASH returned status %02x\n", status);84err = -EINVAL;85goto out;86}8788if (0)89mthca_dbg(dev, "Hash for %pI6 is %04x\n", gid, *hash);9091*index = *hash;92*prev = -1;9394do {95err = mthca_READ_MGM(dev, *index, mgm_mailbox, &status);96if (err)97goto out;98if (status) {99mthca_err(dev, "READ_MGM returned status %02x\n", status);100err = -EINVAL;101goto out;102}103104if (!memcmp(mgm->gid, zero_gid, 16)) {105if (*index != *hash) {106mthca_err(dev, "Found zero MGID in AMGM.\n");107err = -EINVAL;108}109goto out;110}111112if (!memcmp(mgm->gid, gid, 16))113goto out;114115*prev = *index;116*index = be32_to_cpu(mgm->next_gid_index) >> 6;117} while (*index);118119*index = -1;120121out:122mthca_free_mailbox(dev, mailbox);123return err;124}125126int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)127{128struct mthca_dev *dev = to_mdev(ibqp->device);129struct mthca_mailbox *mailbox;130struct mthca_mgm *mgm;131u16 hash;132int index, prev;133int link = 0;134int i;135int err;136u8 status;137138mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);139if (IS_ERR(mailbox))140return PTR_ERR(mailbox);141mgm = mailbox->buf;142143mutex_lock(&dev->mcg_table.mutex);144145err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);146if (err)147goto out;148149if (index != -1) {150if (!memcmp(mgm->gid, zero_gid, 16))151memcpy(mgm->gid, gid->raw, 16);152} else {153link = 1;154155index = mthca_alloc(&dev->mcg_table.alloc);156if (index == -1) {157mthca_err(dev, "No AMGM entries left\n");158err = -ENOMEM;159goto out;160}161162err = mthca_READ_MGM(dev, index, mailbox, &status);163if (err)164goto out;165if (status) {166mthca_err(dev, "READ_MGM returned status %02x\n", status);167err = -EINVAL;168goto out;169}170memset(mgm, 0, sizeof *mgm);171memcpy(mgm->gid, gid->raw, 16);172}173174for (i = 0; i < MTHCA_QP_PER_MGM; ++i)175if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31))) {176mthca_dbg(dev, "QP %06x already a member of MGM\n",177ibqp->qp_num);178err = 0;179goto out;180} else if (!(mgm->qp[i] & cpu_to_be32(1 << 31))) {181mgm->qp[i] = cpu_to_be32(ibqp->qp_num | (1 << 31));182break;183}184185if (i == MTHCA_QP_PER_MGM) {186mthca_err(dev, "MGM at index %x is full.\n", index);187err = -ENOMEM;188goto out;189}190191err = mthca_WRITE_MGM(dev, index, mailbox, &status);192if (err)193goto out;194if (status) {195mthca_err(dev, "WRITE_MGM returned status %02x\n", status);196err = -EINVAL;197goto out;198}199200if (!link)201goto out;202203err = mthca_READ_MGM(dev, prev, mailbox, &status);204if (err)205goto out;206if (status) {207mthca_err(dev, "READ_MGM returned status %02x\n", status);208err = -EINVAL;209goto out;210}211212mgm->next_gid_index = cpu_to_be32(index << 6);213214err = mthca_WRITE_MGM(dev, prev, mailbox, &status);215if (err)216goto out;217if (status) {218mthca_err(dev, "WRITE_MGM returned status %02x\n", status);219err = -EINVAL;220}221222out:223if (err && link && index != -1) {224BUG_ON(index < dev->limits.num_mgms);225mthca_free(&dev->mcg_table.alloc, index);226}227mutex_unlock(&dev->mcg_table.mutex);228229mthca_free_mailbox(dev, mailbox);230return err;231}232233int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)234{235struct mthca_dev *dev = to_mdev(ibqp->device);236struct mthca_mailbox *mailbox;237struct mthca_mgm *mgm;238u16 hash;239int prev, index;240int i, loc;241int err;242u8 status;243244mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);245if (IS_ERR(mailbox))246return PTR_ERR(mailbox);247mgm = mailbox->buf;248249mutex_lock(&dev->mcg_table.mutex);250251err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);252if (err)253goto out;254255if (index == -1) {256mthca_err(dev, "MGID %pI6 not found\n", gid->raw);257err = -EINVAL;258goto out;259}260261for (loc = -1, i = 0; i < MTHCA_QP_PER_MGM; ++i) {262if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31)))263loc = i;264if (!(mgm->qp[i] & cpu_to_be32(1 << 31)))265break;266}267268if (loc == -1) {269mthca_err(dev, "QP %06x not found in MGM\n", ibqp->qp_num);270err = -EINVAL;271goto out;272}273274mgm->qp[loc] = mgm->qp[i - 1];275mgm->qp[i - 1] = 0;276277err = mthca_WRITE_MGM(dev, index, mailbox, &status);278if (err)279goto out;280if (status) {281mthca_err(dev, "WRITE_MGM returned status %02x\n", status);282err = -EINVAL;283goto out;284}285286if (i != 1)287goto out;288289if (prev == -1) {290/* Remove entry from MGM */291int amgm_index_to_free = be32_to_cpu(mgm->next_gid_index) >> 6;292if (amgm_index_to_free) {293err = mthca_READ_MGM(dev, amgm_index_to_free,294mailbox, &status);295if (err)296goto out;297if (status) {298mthca_err(dev, "READ_MGM returned status %02x\n",299status);300err = -EINVAL;301goto out;302}303} else304memset(mgm->gid, 0, 16);305306err = mthca_WRITE_MGM(dev, index, mailbox, &status);307if (err)308goto out;309if (status) {310mthca_err(dev, "WRITE_MGM returned status %02x\n", status);311err = -EINVAL;312goto out;313}314if (amgm_index_to_free) {315BUG_ON(amgm_index_to_free < dev->limits.num_mgms);316mthca_free(&dev->mcg_table.alloc, amgm_index_to_free);317}318} else {319/* Remove entry from AMGM */320int curr_next_index = be32_to_cpu(mgm->next_gid_index) >> 6;321err = mthca_READ_MGM(dev, prev, mailbox, &status);322if (err)323goto out;324if (status) {325mthca_err(dev, "READ_MGM returned status %02x\n", status);326err = -EINVAL;327goto out;328}329330mgm->next_gid_index = cpu_to_be32(curr_next_index << 6);331332err = mthca_WRITE_MGM(dev, prev, mailbox, &status);333if (err)334goto out;335if (status) {336mthca_err(dev, "WRITE_MGM returned status %02x\n", status);337err = -EINVAL;338goto out;339}340BUG_ON(index < dev->limits.num_mgms);341mthca_free(&dev->mcg_table.alloc, index);342}343344out:345mutex_unlock(&dev->mcg_table.mutex);346347mthca_free_mailbox(dev, mailbox);348return err;349}350351int mthca_init_mcg_table(struct mthca_dev *dev)352{353int err;354int table_size = dev->limits.num_mgms + dev->limits.num_amgms;355356err = mthca_alloc_init(&dev->mcg_table.alloc,357table_size,358table_size - 1,359dev->limits.num_mgms);360if (err)361return err;362363mutex_init(&dev->mcg_table.mutex);364365return 0;366}367368void mthca_cleanup_mcg_table(struct mthca_dev *dev)369{370mthca_alloc_cleanup(&dev->mcg_table.alloc);371}372373374