linux/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
   3 * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved.
   4 *
   5 * This software is available to you under a choice of one of two
   6 * licenses.  You may choose to be licensed under the terms of the GNU
   7 * General Public License (GPL) Version 2, available from the file
   8 * COPYING in the main directory of this source tree, or the
   9 * OpenIB.org BSD license below:
  10 *
  11 *     Redistribution and use in source and binary forms, with or
  12 *     without modification, are permitted provided that the following
  13 *     conditions are met:
  14 *
  15 *      - Redistributions of source code must retain the above
  16 *        copyright notice, this list of conditions and the following
  17 *        disclaimer.
  18 *
  19 *      - Redistributions in binary form must reproduce the above
  20 *        copyright notice, this list of conditions and the following
  21 *        disclaimer in the documentation and/or other materials
  22 *        provided with the distribution.
  23 *
  24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31 * SOFTWARE.
  32 */
  33
  34#include <linux/rculist.h>
  35#include <linux/sched.h>
  36
  37#include "ipath_verbs.h"
  38
  39/*
  40 * Global table of GID to attached QPs.
  41 * The table is global to all ipath devices since a send from one QP/device
  42 * needs to be locally routed to any locally attached QPs on the same
  43 * or different device.
  44 */
  45static struct rb_root mcast_tree;
  46static DEFINE_SPINLOCK(mcast_lock);
  47
  48/**
  49 * ipath_mcast_qp_alloc - alloc a struct to link a QP to mcast GID struct
  50 * @qp: the QP to link
  51 */
  52static struct ipath_mcast_qp *ipath_mcast_qp_alloc(struct ipath_qp *qp)
  53{
  54        struct ipath_mcast_qp *mqp;
  55
  56        mqp = kmalloc(sizeof *mqp, GFP_KERNEL);
  57        if (!mqp)
  58                goto bail;
  59
  60        mqp->qp = qp;
  61        atomic_inc(&qp->refcount);
  62
  63bail:
  64        return mqp;
  65}
  66
  67static void ipath_mcast_qp_free(struct ipath_mcast_qp *mqp)
  68{
  69        struct ipath_qp *qp = mqp->qp;
  70
  71        /* Notify ipath_destroy_qp() if it is waiting. */
  72        if (atomic_dec_and_test(&qp->refcount))
  73                wake_up(&qp->wait);
  74
  75        kfree(mqp);
  76}
  77
  78/**
  79 * ipath_mcast_alloc - allocate the multicast GID structure
  80 * @mgid: the multicast GID
  81 *
  82 * A list of QPs will be attached to this structure.
  83 */
  84static struct ipath_mcast *ipath_mcast_alloc(union ib_gid *mgid)
  85{
  86        struct ipath_mcast *mcast;
  87
  88        mcast = kmalloc(sizeof *mcast, GFP_KERNEL);
  89        if (!mcast)
  90                goto bail;
  91
  92        mcast->mgid = *mgid;
  93        INIT_LIST_HEAD(&mcast->qp_list);
  94        init_waitqueue_head(&mcast->wait);
  95        atomic_set(&mcast->refcount, 0);
  96        mcast->n_attached = 0;
  97
  98bail:
  99        return mcast;
 100}
 101
 102static void ipath_mcast_free(struct ipath_mcast *mcast)
 103{
 104        struct ipath_mcast_qp *p, *tmp;
 105
 106        list_for_each_entry_safe(p, tmp, &mcast->qp_list, list)
 107                ipath_mcast_qp_free(p);
 108
 109        kfree(mcast);
 110}
 111
 112/**
 113 * ipath_mcast_find - search the global table for the given multicast GID
 114 * @mgid: the multicast GID to search for
 115 *
 116 * Returns NULL if not found.
 117 *
 118 * The caller is responsible for decrementing the reference count if found.
 119 */
 120struct ipath_mcast *ipath_mcast_find(union ib_gid *mgid)
 121{
 122        struct rb_node *n;
 123        unsigned long flags;
 124        struct ipath_mcast *mcast;
 125
 126        spin_lock_irqsave(&mcast_lock, flags);
 127        n = mcast_tree.rb_node;
 128        while (n) {
 129                int ret;
 130
 131                mcast = rb_entry(n, struct ipath_mcast, rb_node);
 132
 133                ret = memcmp(mgid->raw, mcast->mgid.raw,
 134                             sizeof(union ib_gid));
 135                if (ret < 0)
 136                        n = n->rb_left;
 137                else if (ret > 0)
 138                        n = n->rb_right;
 139                else {
 140                        atomic_inc(&mcast->refcount);
 141                        spin_unlock_irqrestore(&mcast_lock, flags);
 142                        goto bail;
 143                }
 144        }
 145        spin_unlock_irqrestore(&mcast_lock, flags);
 146
 147        mcast = NULL;
 148
 149bail:
 150        return mcast;
 151}
 152
 153/**
 154 * ipath_mcast_add - insert mcast GID into table and attach QP struct
 155 * @mcast: the mcast GID table
 156 * @mqp: the QP to attach
 157 *
 158 * Return zero if both were added.  Return EEXIST if the GID was already in
 159 * the table but the QP was added.  Return ESRCH if the QP was already
 160 * attached and neither structure was added.
 161 */
 162static int ipath_mcast_add(struct ipath_ibdev *dev,
 163                           struct ipath_mcast *mcast,
 164                           struct ipath_mcast_qp *mqp)
 165{
 166        struct rb_node **n = &mcast_tree.rb_node;
 167        struct rb_node *pn = NULL;
 168        int ret;
 169
 170        spin_lock_irq(&mcast_lock);
 171
 172        while (*n) {
 173                struct ipath_mcast *tmcast;
 174                struct ipath_mcast_qp *p;
 175
 176                pn = *n;
 177                tmcast = rb_entry(pn, struct ipath_mcast, rb_node);
 178
 179                ret = memcmp(mcast->mgid.raw, tmcast->mgid.raw,
 180                             sizeof(union ib_gid));
 181                if (ret < 0) {
 182                        n = &pn->rb_left;
 183                        continue;
 184                }
 185                if (ret > 0) {
 186                        n = &pn->rb_right;
 187                        continue;
 188                }
 189
 190                /* Search the QP list to see if this is already there. */
 191                list_for_each_entry_rcu(p, &tmcast->qp_list, list) {
 192                        if (p->qp == mqp->qp) {
 193                                ret = ESRCH;
 194                                goto bail;
 195                        }
 196                }
 197                if (tmcast->n_attached == ib_ipath_max_mcast_qp_attached) {
 198                        ret = ENOMEM;
 199                        goto bail;
 200                }
 201
 202                tmcast->n_attached++;
 203
 204                list_add_tail_rcu(&mqp->list, &tmcast->qp_list);
 205                ret = EEXIST;
 206                goto bail;
 207        }
 208
 209        spin_lock(&dev->n_mcast_grps_lock);
 210        if (dev->n_mcast_grps_allocated == ib_ipath_max_mcast_grps) {
 211                spin_unlock(&dev->n_mcast_grps_lock);
 212                ret = ENOMEM;
 213                goto bail;
 214        }
 215
 216        dev->n_mcast_grps_allocated++;
 217        spin_unlock(&dev->n_mcast_grps_lock);
 218
 219        mcast->n_attached++;
 220
 221        list_add_tail_rcu(&mqp->list, &mcast->qp_list);
 222
 223        atomic_inc(&mcast->refcount);
 224        rb_link_node(&mcast->rb_node, pn, n);
 225        rb_insert_color(&mcast->rb_node, &mcast_tree);
 226
 227        ret = 0;
 228
 229bail:
 230        spin_unlock_irq(&mcast_lock);
 231
 232        return ret;
 233}
 234
 235int ipath_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
 236{
 237        struct ipath_qp *qp = to_iqp(ibqp);
 238        struct ipath_ibdev *dev = to_idev(ibqp->device);
 239        struct ipath_mcast *mcast;
 240        struct ipath_mcast_qp *mqp;
 241        int ret;
 242
 243        /*
 244         * Allocate data structures since its better to do this outside of
 245         * spin locks and it will most likely be needed.
 246         */
 247        mcast = ipath_mcast_alloc(gid);
 248        if (mcast == NULL) {
 249                ret = -ENOMEM;
 250                goto bail;
 251        }
 252        mqp = ipath_mcast_qp_alloc(qp);
 253        if (mqp == NULL) {
 254                ipath_mcast_free(mcast);
 255                ret = -ENOMEM;
 256                goto bail;
 257        }
 258        switch (ipath_mcast_add(dev, mcast, mqp)) {
 259        case ESRCH:
 260                /* Neither was used: can't attach the same QP twice. */
 261                ipath_mcast_qp_free(mqp);
 262                ipath_mcast_free(mcast);
 263                ret = -EINVAL;
 264                goto bail;
 265        case EEXIST:            /* The mcast wasn't used */
 266                ipath_mcast_free(mcast);
 267                break;
 268        case ENOMEM:
 269                /* Exceeded the maximum number of mcast groups. */
 270                ipath_mcast_qp_free(mqp);
 271                ipath_mcast_free(mcast);
 272                ret = -ENOMEM;
 273                goto bail;
 274        default:
 275                break;
 276        }
 277
 278        ret = 0;
 279
 280bail:
 281        return ret;
 282}
 283
 284int ipath_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
 285{
 286        struct ipath_qp *qp = to_iqp(ibqp);
 287        struct ipath_ibdev *dev = to_idev(ibqp->device);
 288        struct ipath_mcast *mcast = NULL;
 289        struct ipath_mcast_qp *p, *tmp;
 290        struct rb_node *n;
 291        int last = 0;
 292        int ret;
 293
 294        spin_lock_irq(&mcast_lock);
 295
 296        /* Find the GID in the mcast table. */
 297        n = mcast_tree.rb_node;
 298        while (1) {
 299                if (n == NULL) {
 300                        spin_unlock_irq(&mcast_lock);
 301                        ret = -EINVAL;
 302                        goto bail;
 303                }
 304
 305                mcast = rb_entry(n, struct ipath_mcast, rb_node);
 306                ret = memcmp(gid->raw, mcast->mgid.raw,
 307                             sizeof(union ib_gid));
 308                if (ret < 0)
 309                        n = n->rb_left;
 310                else if (ret > 0)
 311                        n = n->rb_right;
 312                else
 313                        break;
 314        }
 315
 316        /* Search the QP list. */
 317        list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) {
 318                if (p->qp != qp)
 319                        continue;
 320                /*
 321                 * We found it, so remove it, but don't poison the forward
 322                 * link until we are sure there are no list walkers.
 323                 */
 324                list_del_rcu(&p->list);
 325                mcast->n_attached--;
 326
 327                /* If this was the last attached QP, remove the GID too. */
 328                if (list_empty(&mcast->qp_list)) {
 329                        rb_erase(&mcast->rb_node, &mcast_tree);
 330                        last = 1;
 331                }
 332                break;
 333        }
 334
 335        spin_unlock_irq(&mcast_lock);
 336
 337        if (p) {
 338                /*
 339                 * Wait for any list walkers to finish before freeing the
 340                 * list element.
 341                 */
 342                wait_event(mcast->wait, atomic_read(&mcast->refcount) <= 1);
 343                ipath_mcast_qp_free(p);
 344        }
 345        if (last) {
 346                atomic_dec(&mcast->refcount);
 347                wait_event(mcast->wait, !atomic_read(&mcast->refcount));
 348                ipath_mcast_free(mcast);
 349                spin_lock_irq(&dev->n_mcast_grps_lock);
 350                dev->n_mcast_grps_allocated--;
 351                spin_unlock_irq(&dev->n_mcast_grps_lock);
 352        }
 353
 354        ret = 0;
 355
 356bail:
 357        return ret;
 358}
 359
 360int ipath_mcast_tree_empty(void)
 361{
 362        return mcast_tree.rb_node == NULL;
 363}
 364
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.