linux/drivers/infiniband/hw/mthca/mthca_mcg.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2004 Topspin Communications.  All rights reserved.
   3 *
   4 * This software is available to you under a choice of one of two
   5 * licenses.  You may choose to be licensed under the terms of the GNU
   6 * General Public License (GPL) Version 2, available from the file
   7 * COPYING in the main directory of this source tree, or the
   8 * OpenIB.org BSD license below:
   9 *
  10 *     Redistribution and use in source and binary forms, with or
  11 *     without modification, are permitted provided that the following
  12 *     conditions are met:
  13 *
  14 *      - Redistributions of source code must retain the above
  15 *        copyright notice, this list of conditions and the following
  16 *        disclaimer.
  17 *
  18 *      - Redistributions in binary form must reproduce the above
  19 *        copyright notice, this list of conditions and the following
  20 *        disclaimer in the documentation and/or other materials
  21 *        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 OF
  25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  30 * 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 must
  50 * 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 = -1
  55 * and *mgm holds MGM entry.
  56 *
  57 * if GID is found in AMGM, *index = index in AMGM, *prev = index of
  58 * previous entry in hash chain and *mgm holds AMGM entry.
  59 *
  60 * If no AMGM exists for given gid, *index = -1, *prev = index of last
  61 * 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        u8 status;
  72
  73        mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
  74        if (IS_ERR(mailbox))
  75                return -ENOMEM;
  76        mgid = mailbox->buf;
  77
  78        memcpy(mgid, gid, 16);
  79
  80        err = mthca_MGID_HASH(dev, mailbox, hash, &status);
  81        if (err)
  82                goto out;
  83        if (status) {
  84                mthca_err(dev, "MGID_HASH returned status %02x\n", status);
  85                err = -EINVAL;
  86                goto out;
  87        }
  88
  89        if (0)
  90                mthca_dbg(dev, "Hash for %pI6 is %04x\n", gid, *hash);
  91
  92        *index = *hash;
  93        *prev  = -1;
  94
  95        do {
  96                err = mthca_READ_MGM(dev, *index, mgm_mailbox, &status);
  97                if (err)
  98                        goto out;
  99                if (status) {
 100                        mthca_err(dev, "READ_MGM returned status %02x\n", status);
 101                        err = -EINVAL;
 102                        goto out;
 103                }
 104
 105                if (!memcmp(mgm->gid, zero_gid, 16)) {
 106                        if (*index != *hash) {
 107                                mthca_err(dev, "Found zero MGID in AMGM.\n");
 108                                err = -EINVAL;
 109                        }
 110                        goto out;
 111                }
 112
 113                if (!memcmp(mgm->gid, gid, 16))
 114                        goto out;
 115
 116                *prev = *index;
 117                *index = be32_to_cpu(mgm->next_gid_index) >> 6;
 118        } while (*index);
 119
 120        *index = -1;
 121
 122 out:
 123        mthca_free_mailbox(dev, mailbox);
 124        return err;
 125}
 126
 127int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
 128{
 129        struct mthca_dev *dev = to_mdev(ibqp->device);
 130        struct mthca_mailbox *mailbox;
 131        struct mthca_mgm *mgm;
 132        u16 hash;
 133        int index, prev;
 134        int link = 0;
 135        int i;
 136        int err;
 137        u8 status;
 138
 139        mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
 140        if (IS_ERR(mailbox))
 141                return PTR_ERR(mailbox);
 142        mgm = mailbox->buf;
 143
 144        mutex_lock(&dev->mcg_table.mutex);
 145
 146        err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);
 147        if (err)
 148                goto out;
 149
 150        if (index != -1) {
 151                if (!memcmp(mgm->gid, zero_gid, 16))
 152                        memcpy(mgm->gid, gid->raw, 16);
 153        } else {
 154                link = 1;
 155
 156                index = mthca_alloc(&dev->mcg_table.alloc);
 157                if (index == -1) {
 158                        mthca_err(dev, "No AMGM entries left\n");
 159                        err = -ENOMEM;
 160                        goto out;
 161                }
 162
 163                err = mthca_READ_MGM(dev, index, mailbox, &status);
 164                if (err)
 165                        goto out;
 166                if (status) {
 167                        mthca_err(dev, "READ_MGM returned status %02x\n", status);
 168                        err = -EINVAL;
 169                        goto out;
 170                }
 171                memset(mgm, 0, sizeof *mgm);
 172                memcpy(mgm->gid, gid->raw, 16);
 173        }
 174
 175        for (i = 0; i < MTHCA_QP_PER_MGM; ++i)
 176                if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31))) {
 177                        mthca_dbg(dev, "QP %06x already a member of MGM\n",
 178                                  ibqp->qp_num);
 179                        err = 0;
 180                        goto out;
 181                } else if (!(mgm->qp[i] & cpu_to_be32(1 << 31))) {
 182                        mgm->qp[i] = cpu_to_be32(ibqp->qp_num | (1 << 31));
 183                        break;
 184                }
 185
 186        if (i == MTHCA_QP_PER_MGM) {
 187                mthca_err(dev, "MGM at index %x is full.\n", index);
 188                err = -ENOMEM;
 189                goto out;
 190        }
 191
 192        err = mthca_WRITE_MGM(dev, index, mailbox, &status);
 193        if (err)
 194                goto out;
 195        if (status) {
 196                mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
 197                err = -EINVAL;
 198                goto out;
 199        }
 200
 201        if (!link)
 202                goto out;
 203
 204        err = mthca_READ_MGM(dev, prev, mailbox, &status);
 205        if (err)
 206                goto out;
 207        if (status) {
 208                mthca_err(dev, "READ_MGM returned status %02x\n", status);
 209                err = -EINVAL;
 210                goto out;
 211        }
 212
 213        mgm->next_gid_index = cpu_to_be32(index << 6);
 214
 215        err = mthca_WRITE_MGM(dev, prev, mailbox, &status);
 216        if (err)
 217                goto out;
 218        if (status) {
 219                mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
 220                err = -EINVAL;
 221        }
 222
 223 out:
 224        if (err && link && index != -1) {
 225                BUG_ON(index < dev->limits.num_mgms);
 226                mthca_free(&dev->mcg_table.alloc, index);
 227        }
 228        mutex_unlock(&dev->mcg_table.mutex);
 229
 230        mthca_free_mailbox(dev, mailbox);
 231        return err;
 232}
 233
 234int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
 235{
 236        struct mthca_dev *dev = to_mdev(ibqp->device);
 237        struct mthca_mailbox *mailbox;
 238        struct mthca_mgm *mgm;
 239        u16 hash;
 240        int prev, index;
 241        int i, loc;
 242        int err;
 243        u8 status;
 244
 245        mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
 246        if (IS_ERR(mailbox))
 247                return PTR_ERR(mailbox);
 248        mgm = mailbox->buf;
 249
 250        mutex_lock(&dev->mcg_table.mutex);
 251
 252        err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);
 253        if (err)
 254                goto out;
 255
 256        if (index == -1) {
 257                mthca_err(dev, "MGID %pI6 not found\n", gid->raw);
 258                err = -EINVAL;
 259                goto out;
 260        }
 261
 262        for (loc = -1, i = 0; i < MTHCA_QP_PER_MGM; ++i) {
 263                if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31)))
 264                        loc = i;
 265                if (!(mgm->qp[i] & cpu_to_be32(1 << 31)))
 266                        break;
 267        }
 268
 269        if (loc == -1) {
 270                mthca_err(dev, "QP %06x not found in MGM\n", ibqp->qp_num);
 271                err = -EINVAL;
 272                goto out;
 273        }
 274
 275        mgm->qp[loc]   = mgm->qp[i - 1];
 276        mgm->qp[i - 1] = 0;
 277
 278        err = mthca_WRITE_MGM(dev, index, mailbox, &status);
 279        if (err)
 280                goto out;
 281        if (status) {
 282                mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
 283                err = -EINVAL;
 284                goto out;
 285        }
 286
 287        if (i != 1)
 288                goto out;
 289
 290        if (prev == -1) {
 291                /* Remove entry from MGM */
 292                int amgm_index_to_free = be32_to_cpu(mgm->next_gid_index) >> 6;
 293                if (amgm_index_to_free) {
 294                        err = mthca_READ_MGM(dev, amgm_index_to_free,
 295                                             mailbox, &status);
 296                        if (err)
 297                                goto out;
 298                        if (status) {
 299                                mthca_err(dev, "READ_MGM returned status %02x\n",
 300                                          status);
 301                                err = -EINVAL;
 302                                goto out;
 303                        }
 304                } else
 305                        memset(mgm->gid, 0, 16);
 306
 307                err = mthca_WRITE_MGM(dev, index, mailbox, &status);
 308                if (err)
 309                        goto out;
 310                if (status) {
 311                        mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
 312                        err = -EINVAL;
 313                        goto out;
 314                }
 315                if (amgm_index_to_free) {
 316                        BUG_ON(amgm_index_to_free < dev->limits.num_mgms);
 317                        mthca_free(&dev->mcg_table.alloc, amgm_index_to_free);
 318                }
 319        } else {
 320                /* Remove entry from AMGM */
 321                int curr_next_index = be32_to_cpu(mgm->next_gid_index) >> 6;
 322                err = mthca_READ_MGM(dev, prev, mailbox, &status);
 323                if (err)
 324                        goto out;
 325                if (status) {
 326                        mthca_err(dev, "READ_MGM returned status %02x\n", status);
 327                        err = -EINVAL;
 328                        goto out;
 329                }
 330
 331                mgm->next_gid_index = cpu_to_be32(curr_next_index << 6);
 332
 333                err = mthca_WRITE_MGM(dev, prev, mailbox, &status);
 334                if (err)
 335                        goto out;
 336                if (status) {
 337                        mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
 338                        err = -EINVAL;
 339                        goto out;
 340                }
 341                BUG_ON(index < dev->limits.num_mgms);
 342                mthca_free(&dev->mcg_table.alloc, index);
 343        }
 344
 345 out:
 346        mutex_unlock(&dev->mcg_table.mutex);
 347
 348        mthca_free_mailbox(dev, mailbox);
 349        return err;
 350}
 351
 352int mthca_init_mcg_table(struct mthca_dev *dev)
 353{
 354        int err;
 355        int table_size = dev->limits.num_mgms + dev->limits.num_amgms;
 356
 357        err = mthca_alloc_init(&dev->mcg_table.alloc,
 358                               table_size,
 359                               table_size - 1,
 360                               dev->limits.num_mgms);
 361        if (err)
 362                return err;
 363
 364        mutex_init(&dev->mcg_table.mutex);
 365
 366        return 0;
 367}
 368
 369void mthca_cleanup_mcg_table(struct mthca_dev *dev)
 370{
 371        mthca_alloc_cleanup(&dev->mcg_table.alloc);
 372}
 373