1219820Sjeff/* 2219820Sjeff * Copyright (c) 2004 Topspin Communications. All rights reserved. 3219820Sjeff * 4219820Sjeff * This software is available to you under a choice of one of two 5219820Sjeff * licenses. You may choose to be licensed under the terms of the GNU 6219820Sjeff * General Public License (GPL) Version 2, available from the file 7219820Sjeff * COPYING in the main directory of this source tree, or the 8219820Sjeff * OpenIB.org BSD license below: 9219820Sjeff * 10219820Sjeff * Redistribution and use in source and binary forms, with or 11219820Sjeff * without modification, are permitted provided that the following 12219820Sjeff * conditions are met: 13219820Sjeff * 14219820Sjeff * - Redistributions of source code must retain the above 15219820Sjeff * copyright notice, this list of conditions and the following 16219820Sjeff * disclaimer. 17219820Sjeff * 18219820Sjeff * - Redistributions in binary form must reproduce the above 19219820Sjeff * copyright notice, this list of conditions and the following 20219820Sjeff * disclaimer in the documentation and/or other materials 21219820Sjeff * provided with the distribution. 22219820Sjeff * 23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30219820Sjeff * SOFTWARE. 31219820Sjeff */ 32219820Sjeff 33219820Sjeff#include <linux/string.h> 34219820Sjeff#include <linux/slab.h> 35219820Sjeff 36219820Sjeff#include "mthca_dev.h" 37219820Sjeff#include "mthca_cmd.h" 38219820Sjeff 39219820Sjeffstruct mthca_mgm { 40219820Sjeff __be32 next_gid_index; 41219820Sjeff u32 reserved[3]; 42219820Sjeff u8 gid[16]; 43219820Sjeff __be32 qp[MTHCA_QP_PER_MGM]; 44219820Sjeff}; 45219820Sjeff 46219820Sjeffstatic const u8 zero_gid[16]; /* automatically initialized to 0 */ 47219820Sjeff 48219820Sjeff/* 49219820Sjeff * Caller must hold MCG table semaphore. gid and mgm parameters must 50219820Sjeff * be properly aligned for command interface. 51219820Sjeff * 52219820Sjeff * Returns 0 unless a firmware command error occurs. 53219820Sjeff * 54219820Sjeff * If GID is found in MGM or MGM is empty, *index = *hash, *prev = -1 55219820Sjeff * and *mgm holds MGM entry. 56219820Sjeff * 57219820Sjeff * if GID is found in AMGM, *index = index in AMGM, *prev = index of 58219820Sjeff * previous entry in hash chain and *mgm holds AMGM entry. 59219820Sjeff * 60219820Sjeff * If no AMGM exists for given gid, *index = -1, *prev = index of last 61219820Sjeff * entry in hash chain and *mgm holds end of hash chain. 62219820Sjeff */ 63219820Sjeffstatic int find_mgm(struct mthca_dev *dev, 64219820Sjeff u8 *gid, struct mthca_mailbox *mgm_mailbox, 65219820Sjeff u16 *hash, int *prev, int *index) 66219820Sjeff{ 67219820Sjeff struct mthca_mailbox *mailbox; 68219820Sjeff struct mthca_mgm *mgm = mgm_mailbox->buf; 69219820Sjeff u8 *mgid; 70219820Sjeff int err; 71219820Sjeff u8 status; 72219820Sjeff 73219820Sjeff mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); 74219820Sjeff if (IS_ERR(mailbox)) 75219820Sjeff return -ENOMEM; 76219820Sjeff mgid = mailbox->buf; 77219820Sjeff 78219820Sjeff memcpy(mgid, gid, 16); 79219820Sjeff 80219820Sjeff err = mthca_MGID_HASH(dev, mailbox, hash, &status); 81219820Sjeff if (err) 82219820Sjeff goto out; 83219820Sjeff if (status) { 84219820Sjeff mthca_err(dev, "MGID_HASH returned status %02x\n", status); 85219820Sjeff err = -EINVAL; 86219820Sjeff goto out; 87219820Sjeff } 88219820Sjeff 89219820Sjeff if (0) 90219820Sjeff mthca_dbg(dev, "Hash for %pI6 is %04x\n", gid, *hash); 91219820Sjeff 92219820Sjeff *index = *hash; 93219820Sjeff *prev = -1; 94219820Sjeff 95219820Sjeff do { 96219820Sjeff err = mthca_READ_MGM(dev, *index, mgm_mailbox, &status); 97219820Sjeff if (err) 98219820Sjeff goto out; 99219820Sjeff if (status) { 100219820Sjeff mthca_err(dev, "READ_MGM returned status %02x\n", status); 101219820Sjeff err = -EINVAL; 102219820Sjeff goto out; 103219820Sjeff } 104219820Sjeff 105219820Sjeff if (!memcmp(mgm->gid, zero_gid, 16)) { 106219820Sjeff if (*index != *hash) { 107219820Sjeff mthca_err(dev, "Found zero MGID in AMGM.\n"); 108219820Sjeff err = -EINVAL; 109219820Sjeff } 110219820Sjeff goto out; 111219820Sjeff } 112219820Sjeff 113219820Sjeff if (!memcmp(mgm->gid, gid, 16)) 114219820Sjeff goto out; 115219820Sjeff 116219820Sjeff *prev = *index; 117219820Sjeff *index = be32_to_cpu(mgm->next_gid_index) >> 6; 118219820Sjeff } while (*index); 119219820Sjeff 120219820Sjeff *index = -1; 121219820Sjeff 122219820Sjeff out: 123219820Sjeff mthca_free_mailbox(dev, mailbox); 124219820Sjeff return err; 125219820Sjeff} 126219820Sjeff 127219820Sjeffint mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) 128219820Sjeff{ 129219820Sjeff struct mthca_dev *dev = to_mdev(ibqp->device); 130219820Sjeff struct mthca_mailbox *mailbox; 131219820Sjeff struct mthca_mgm *mgm; 132219820Sjeff u16 hash; 133219820Sjeff int index, prev; 134219820Sjeff int link = 0; 135219820Sjeff int i; 136219820Sjeff int err; 137219820Sjeff u8 status; 138219820Sjeff 139219820Sjeff mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); 140219820Sjeff if (IS_ERR(mailbox)) 141219820Sjeff return PTR_ERR(mailbox); 142219820Sjeff mgm = mailbox->buf; 143219820Sjeff 144219820Sjeff mutex_lock(&dev->mcg_table.mutex); 145219820Sjeff 146219820Sjeff err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index); 147219820Sjeff if (err) 148219820Sjeff goto out; 149219820Sjeff 150219820Sjeff if (index != -1) { 151219820Sjeff if (!memcmp(mgm->gid, zero_gid, 16)) 152219820Sjeff memcpy(mgm->gid, gid->raw, 16); 153219820Sjeff } else { 154219820Sjeff link = 1; 155219820Sjeff 156219820Sjeff index = mthca_alloc(&dev->mcg_table.alloc); 157219820Sjeff if (index == -1) { 158219820Sjeff mthca_err(dev, "No AMGM entries left\n"); 159219820Sjeff err = -ENOMEM; 160219820Sjeff goto out; 161219820Sjeff } 162219820Sjeff 163219820Sjeff err = mthca_READ_MGM(dev, index, mailbox, &status); 164219820Sjeff if (err) 165219820Sjeff goto out; 166219820Sjeff if (status) { 167219820Sjeff mthca_err(dev, "READ_MGM returned status %02x\n", status); 168219820Sjeff err = -EINVAL; 169219820Sjeff goto out; 170219820Sjeff } 171219820Sjeff memset(mgm, 0, sizeof *mgm); 172219820Sjeff memcpy(mgm->gid, gid->raw, 16); 173219820Sjeff } 174219820Sjeff 175219820Sjeff for (i = 0; i < MTHCA_QP_PER_MGM; ++i) 176219820Sjeff if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31))) { 177219820Sjeff mthca_dbg(dev, "QP %06x already a member of MGM\n", 178219820Sjeff ibqp->qp_num); 179219820Sjeff err = 0; 180219820Sjeff goto out; 181219820Sjeff } else if (!(mgm->qp[i] & cpu_to_be32(1 << 31))) { 182219820Sjeff mgm->qp[i] = cpu_to_be32(ibqp->qp_num | (1 << 31)); 183219820Sjeff break; 184219820Sjeff } 185219820Sjeff 186219820Sjeff if (i == MTHCA_QP_PER_MGM) { 187219820Sjeff mthca_err(dev, "MGM at index %x is full.\n", index); 188219820Sjeff err = -ENOMEM; 189219820Sjeff goto out; 190219820Sjeff } 191219820Sjeff 192219820Sjeff err = mthca_WRITE_MGM(dev, index, mailbox, &status); 193219820Sjeff if (err) 194219820Sjeff goto out; 195219820Sjeff if (status) { 196219820Sjeff mthca_err(dev, "WRITE_MGM returned status %02x\n", status); 197219820Sjeff err = -EINVAL; 198219820Sjeff goto out; 199219820Sjeff } 200219820Sjeff 201219820Sjeff if (!link) 202219820Sjeff goto out; 203219820Sjeff 204219820Sjeff err = mthca_READ_MGM(dev, prev, mailbox, &status); 205219820Sjeff if (err) 206219820Sjeff goto out; 207219820Sjeff if (status) { 208219820Sjeff mthca_err(dev, "READ_MGM returned status %02x\n", status); 209219820Sjeff err = -EINVAL; 210219820Sjeff goto out; 211219820Sjeff } 212219820Sjeff 213219820Sjeff mgm->next_gid_index = cpu_to_be32(index << 6); 214219820Sjeff 215219820Sjeff err = mthca_WRITE_MGM(dev, prev, mailbox, &status); 216219820Sjeff if (err) 217219820Sjeff goto out; 218219820Sjeff if (status) { 219219820Sjeff mthca_err(dev, "WRITE_MGM returned status %02x\n", status); 220219820Sjeff err = -EINVAL; 221219820Sjeff } 222219820Sjeff 223219820Sjeff out: 224219820Sjeff if (err && link && index != -1) { 225219820Sjeff BUG_ON(index < dev->limits.num_mgms); 226219820Sjeff mthca_free(&dev->mcg_table.alloc, index); 227219820Sjeff } 228219820Sjeff mutex_unlock(&dev->mcg_table.mutex); 229219820Sjeff 230219820Sjeff mthca_free_mailbox(dev, mailbox); 231219820Sjeff return err; 232219820Sjeff} 233219820Sjeff 234219820Sjeffint mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) 235219820Sjeff{ 236219820Sjeff struct mthca_dev *dev = to_mdev(ibqp->device); 237219820Sjeff struct mthca_mailbox *mailbox; 238219820Sjeff struct mthca_mgm *mgm; 239219820Sjeff u16 hash; 240219820Sjeff int prev, index; 241219820Sjeff int i, loc; 242219820Sjeff int err; 243219820Sjeff u8 status; 244219820Sjeff 245219820Sjeff mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); 246219820Sjeff if (IS_ERR(mailbox)) 247219820Sjeff return PTR_ERR(mailbox); 248219820Sjeff mgm = mailbox->buf; 249219820Sjeff 250219820Sjeff mutex_lock(&dev->mcg_table.mutex); 251219820Sjeff 252219820Sjeff err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index); 253219820Sjeff if (err) 254219820Sjeff goto out; 255219820Sjeff 256219820Sjeff if (index == -1) { 257219820Sjeff mthca_err(dev, "MGID %pI6 not found\n", gid->raw); 258219820Sjeff err = -EINVAL; 259219820Sjeff goto out; 260219820Sjeff } 261219820Sjeff 262219820Sjeff for (loc = -1, i = 0; i < MTHCA_QP_PER_MGM; ++i) { 263219820Sjeff if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31))) 264219820Sjeff loc = i; 265219820Sjeff if (!(mgm->qp[i] & cpu_to_be32(1 << 31))) 266219820Sjeff break; 267219820Sjeff } 268219820Sjeff 269219820Sjeff if (loc == -1) { 270219820Sjeff mthca_err(dev, "QP %06x not found in MGM\n", ibqp->qp_num); 271219820Sjeff err = -EINVAL; 272219820Sjeff goto out; 273219820Sjeff } 274219820Sjeff 275219820Sjeff mgm->qp[loc] = mgm->qp[i - 1]; 276219820Sjeff mgm->qp[i - 1] = 0; 277219820Sjeff 278219820Sjeff err = mthca_WRITE_MGM(dev, index, mailbox, &status); 279219820Sjeff if (err) 280219820Sjeff goto out; 281219820Sjeff if (status) { 282219820Sjeff mthca_err(dev, "WRITE_MGM returned status %02x\n", status); 283219820Sjeff err = -EINVAL; 284219820Sjeff goto out; 285219820Sjeff } 286219820Sjeff 287219820Sjeff if (i != 1) 288219820Sjeff goto out; 289219820Sjeff 290219820Sjeff if (prev == -1) { 291219820Sjeff /* Remove entry from MGM */ 292219820Sjeff int amgm_index_to_free = be32_to_cpu(mgm->next_gid_index) >> 6; 293219820Sjeff if (amgm_index_to_free) { 294219820Sjeff err = mthca_READ_MGM(dev, amgm_index_to_free, 295219820Sjeff mailbox, &status); 296219820Sjeff if (err) 297219820Sjeff goto out; 298219820Sjeff if (status) { 299219820Sjeff mthca_err(dev, "READ_MGM returned status %02x\n", 300219820Sjeff status); 301219820Sjeff err = -EINVAL; 302219820Sjeff goto out; 303219820Sjeff } 304219820Sjeff } else 305219820Sjeff memset(mgm->gid, 0, 16); 306219820Sjeff 307219820Sjeff err = mthca_WRITE_MGM(dev, index, mailbox, &status); 308219820Sjeff if (err) 309219820Sjeff goto out; 310219820Sjeff if (status) { 311219820Sjeff mthca_err(dev, "WRITE_MGM returned status %02x\n", status); 312219820Sjeff err = -EINVAL; 313219820Sjeff goto out; 314219820Sjeff } 315219820Sjeff if (amgm_index_to_free) { 316219820Sjeff BUG_ON(amgm_index_to_free < dev->limits.num_mgms); 317219820Sjeff mthca_free(&dev->mcg_table.alloc, amgm_index_to_free); 318219820Sjeff } 319219820Sjeff } else { 320219820Sjeff /* Remove entry from AMGM */ 321219820Sjeff int curr_next_index = be32_to_cpu(mgm->next_gid_index) >> 6; 322219820Sjeff err = mthca_READ_MGM(dev, prev, mailbox, &status); 323219820Sjeff if (err) 324219820Sjeff goto out; 325219820Sjeff if (status) { 326219820Sjeff mthca_err(dev, "READ_MGM returned status %02x\n", status); 327219820Sjeff err = -EINVAL; 328219820Sjeff goto out; 329219820Sjeff } 330219820Sjeff 331219820Sjeff mgm->next_gid_index = cpu_to_be32(curr_next_index << 6); 332219820Sjeff 333219820Sjeff err = mthca_WRITE_MGM(dev, prev, mailbox, &status); 334219820Sjeff if (err) 335219820Sjeff goto out; 336219820Sjeff if (status) { 337219820Sjeff mthca_err(dev, "WRITE_MGM returned status %02x\n", status); 338219820Sjeff err = -EINVAL; 339219820Sjeff goto out; 340219820Sjeff } 341219820Sjeff BUG_ON(index < dev->limits.num_mgms); 342219820Sjeff mthca_free(&dev->mcg_table.alloc, index); 343219820Sjeff } 344219820Sjeff 345219820Sjeff out: 346219820Sjeff mutex_unlock(&dev->mcg_table.mutex); 347219820Sjeff 348219820Sjeff mthca_free_mailbox(dev, mailbox); 349219820Sjeff return err; 350219820Sjeff} 351219820Sjeff 352219820Sjeffint mthca_init_mcg_table(struct mthca_dev *dev) 353219820Sjeff{ 354219820Sjeff int err; 355219820Sjeff int table_size = dev->limits.num_mgms + dev->limits.num_amgms; 356219820Sjeff 357219820Sjeff err = mthca_alloc_init(&dev->mcg_table.alloc, 358219820Sjeff table_size, 359219820Sjeff table_size - 1, 360219820Sjeff dev->limits.num_mgms); 361219820Sjeff if (err) 362219820Sjeff return err; 363219820Sjeff 364219820Sjeff mutex_init(&dev->mcg_table.mutex); 365219820Sjeff 366219820Sjeff return 0; 367219820Sjeff} 368219820Sjeff 369219820Sjeffvoid mthca_cleanup_mcg_table(struct mthca_dev *dev) 370219820Sjeff{ 371219820Sjeff mthca_alloc_cleanup(&dev->mcg_table.alloc); 372219820Sjeff} 373