336 lines · c
1/*2 * Copyright (c) 2004 Topspin Communications. All rights reserved.3 *4 * This software is available to you under a choice of one of two5 * licenses. You may choose to be licensed under the terms of the GNU6 * General Public License (GPL) Version 2, available from the file7 * COPYING in the main directory of this source tree, or the8 * OpenIB.org BSD license below:9 *10 * Redistribution and use in source and binary forms, with or11 * without modification, are permitted provided that the following12 * conditions are met:13 *14 * - Redistributions of source code must retain the above15 * copyright notice, this list of conditions and the following16 * disclaimer.17 *18 * - Redistributions in binary form must reproduce the above19 * copyright notice, this list of conditions and the following20 * disclaimer in the documentation and/or other materials21 * provided with the distribution.22 *23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE30 * SOFTWARE.31 */32 33#include <linux/string.h>34#include <linux/gfp.h>35 36#include "mthca_dev.h"37#include "mthca_cmd.h"38 39struct mthca_mgm {40 __be32 next_gid_index;41 u32 reserved[3];42 u8 gid[16];43 __be32 qp[MTHCA_QP_PER_MGM];44};45 46static const u8 zero_gid[16]; /* automatically initialized to 0 */47 48/*49 * Caller must hold MCG table semaphore. gid and mgm parameters must50 * be properly aligned for command interface.51 *52 * Returns 0 unless a firmware command error occurs.53 *54 * If GID is found in MGM or MGM is empty, *index = *hash, *prev = -155 * and *mgm holds MGM entry.56 *57 * if GID is found in AMGM, *index = index in AMGM, *prev = index of58 * previous entry in hash chain and *mgm holds AMGM entry.59 *60 * If no AMGM exists for given gid, *index = -1, *prev = index of last61 * entry in hash chain and *mgm holds end of hash chain.62 */63static int find_mgm(struct mthca_dev *dev,64 u8 *gid, struct mthca_mailbox *mgm_mailbox,65 u16 *hash, int *prev, int *index)66{67 struct mthca_mailbox *mailbox;68 struct mthca_mgm *mgm = mgm_mailbox->buf;69 u8 *mgid;70 int err;71 72 mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);73 if (IS_ERR(mailbox))74 return -ENOMEM;75 mgid = mailbox->buf;76 77 memcpy(mgid, gid, 16);78 79 err = mthca_MGID_HASH(dev, mailbox, hash);80 if (err) {81 mthca_err(dev, "MGID_HASH failed (%d)\n", err);82 goto out;83 }84 85 if (0)86 mthca_dbg(dev, "Hash for %pI6 is %04x\n", gid, *hash);87 88 *index = *hash;89 *prev = -1;90 91 do {92 err = mthca_READ_MGM(dev, *index, mgm_mailbox);93 if (err) {94 mthca_err(dev, "READ_MGM failed (%d)\n", err);95 goto out;96 }97 98 if (!memcmp(mgm->gid, zero_gid, 16)) {99 if (*index != *hash) {100 mthca_err(dev, "Found zero MGID in AMGM.\n");101 err = -EINVAL;102 }103 goto out;104 }105 106 if (!memcmp(mgm->gid, gid, 16))107 goto out;108 109 *prev = *index;110 *index = be32_to_cpu(mgm->next_gid_index) >> 6;111 } while (*index);112 113 *index = -1;114 115 out:116 mthca_free_mailbox(dev, mailbox);117 return err;118}119 120int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)121{122 struct mthca_dev *dev = to_mdev(ibqp->device);123 struct mthca_mailbox *mailbox;124 struct mthca_mgm *mgm;125 u16 hash;126 int index, prev;127 int link = 0;128 int i;129 int err;130 131 mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);132 if (IS_ERR(mailbox))133 return PTR_ERR(mailbox);134 mgm = mailbox->buf;135 136 mutex_lock(&dev->mcg_table.mutex);137 138 err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);139 if (err)140 goto out;141 142 if (index != -1) {143 if (!memcmp(mgm->gid, zero_gid, 16))144 memcpy(mgm->gid, gid->raw, 16);145 } else {146 link = 1;147 148 index = mthca_alloc(&dev->mcg_table.alloc);149 if (index == -1) {150 mthca_err(dev, "No AMGM entries left\n");151 err = -ENOMEM;152 goto out;153 }154 155 err = mthca_READ_MGM(dev, index, mailbox);156 if (err) {157 mthca_err(dev, "READ_MGM failed (%d)\n", err);158 goto out;159 }160 memset(mgm, 0, sizeof *mgm);161 memcpy(mgm->gid, gid->raw, 16);162 }163 164 for (i = 0; i < MTHCA_QP_PER_MGM; ++i)165 if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31))) {166 mthca_dbg(dev, "QP %06x already a member of MGM\n",167 ibqp->qp_num);168 err = 0;169 goto out;170 } else if (!(mgm->qp[i] & cpu_to_be32(1 << 31))) {171 mgm->qp[i] = cpu_to_be32(ibqp->qp_num | (1 << 31));172 break;173 }174 175 if (i == MTHCA_QP_PER_MGM) {176 mthca_err(dev, "MGM at index %x is full.\n", index);177 err = -ENOMEM;178 goto out;179 }180 181 err = mthca_WRITE_MGM(dev, index, mailbox);182 if (err) {183 mthca_err(dev, "WRITE_MGM failed %d\n", err);184 err = -EINVAL;185 goto out;186 }187 188 if (!link)189 goto out;190 191 err = mthca_READ_MGM(dev, prev, mailbox);192 if (err) {193 mthca_err(dev, "READ_MGM failed %d\n", err);194 goto out;195 }196 197 mgm->next_gid_index = cpu_to_be32(index << 6);198 199 err = mthca_WRITE_MGM(dev, prev, mailbox);200 if (err)201 mthca_err(dev, "WRITE_MGM returned %d\n", err);202 203 out:204 if (err && link && index != -1) {205 BUG_ON(index < dev->limits.num_mgms);206 mthca_free(&dev->mcg_table.alloc, index);207 }208 mutex_unlock(&dev->mcg_table.mutex);209 210 mthca_free_mailbox(dev, mailbox);211 return err;212}213 214int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)215{216 struct mthca_dev *dev = to_mdev(ibqp->device);217 struct mthca_mailbox *mailbox;218 struct mthca_mgm *mgm;219 u16 hash;220 int prev, index;221 int i, loc;222 int err;223 224 mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);225 if (IS_ERR(mailbox))226 return PTR_ERR(mailbox);227 mgm = mailbox->buf;228 229 mutex_lock(&dev->mcg_table.mutex);230 231 err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);232 if (err)233 goto out;234 235 if (index == -1) {236 mthca_err(dev, "MGID %pI6 not found\n", gid->raw);237 err = -EINVAL;238 goto out;239 }240 241 for (loc = -1, i = 0; i < MTHCA_QP_PER_MGM; ++i) {242 if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31)))243 loc = i;244 if (!(mgm->qp[i] & cpu_to_be32(1 << 31)))245 break;246 }247 248 if (loc == -1) {249 mthca_err(dev, "QP %06x not found in MGM\n", ibqp->qp_num);250 err = -EINVAL;251 goto out;252 }253 254 mgm->qp[loc] = mgm->qp[i - 1];255 mgm->qp[i - 1] = 0;256 257 err = mthca_WRITE_MGM(dev, index, mailbox);258 if (err) {259 mthca_err(dev, "WRITE_MGM returned %d\n", err);260 goto out;261 }262 263 if (i != 1)264 goto out;265 266 if (prev == -1) {267 /* Remove entry from MGM */268 int amgm_index_to_free = be32_to_cpu(mgm->next_gid_index) >> 6;269 if (amgm_index_to_free) {270 err = mthca_READ_MGM(dev, amgm_index_to_free,271 mailbox);272 if (err) {273 mthca_err(dev, "READ_MGM returned %d\n", err);274 goto out;275 }276 } else277 memset(mgm->gid, 0, 16);278 279 err = mthca_WRITE_MGM(dev, index, mailbox);280 if (err) {281 mthca_err(dev, "WRITE_MGM returned %d\n", err);282 goto out;283 }284 if (amgm_index_to_free) {285 BUG_ON(amgm_index_to_free < dev->limits.num_mgms);286 mthca_free(&dev->mcg_table.alloc, amgm_index_to_free);287 }288 } else {289 /* Remove entry from AMGM */290 int curr_next_index = be32_to_cpu(mgm->next_gid_index) >> 6;291 err = mthca_READ_MGM(dev, prev, mailbox);292 if (err) {293 mthca_err(dev, "READ_MGM returned %d\n", err);294 goto out;295 }296 297 mgm->next_gid_index = cpu_to_be32(curr_next_index << 6);298 299 err = mthca_WRITE_MGM(dev, prev, mailbox);300 if (err) {301 mthca_err(dev, "WRITE_MGM returned %d\n", err);302 goto out;303 }304 BUG_ON(index < dev->limits.num_mgms);305 mthca_free(&dev->mcg_table.alloc, index);306 }307 308 out:309 mutex_unlock(&dev->mcg_table.mutex);310 311 mthca_free_mailbox(dev, mailbox);312 return err;313}314 315int mthca_init_mcg_table(struct mthca_dev *dev)316{317 int err;318 int table_size = dev->limits.num_mgms + dev->limits.num_amgms;319 320 err = mthca_alloc_init(&dev->mcg_table.alloc,321 table_size,322 table_size - 1,323 dev->limits.num_mgms);324 if (err)325 return err;326 327 mutex_init(&dev->mcg_table.mutex);328 329 return 0;330}331 332void mthca_cleanup_mcg_table(struct mthca_dev *dev)333{334 mthca_alloc_cleanup(&dev->mcg_table.alloc);335}336