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 39 struct mthca_mgm { 40 __be32 next_gid_index; 41 u32 reserved[3]; 42 u8 gid[16]; 43 __be32 qp[MTHCA_QP_PER_MGM]; 44 }; 45 46 static 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 */ 63 static 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 120 int 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 214 int 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 } else 277 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 315 int 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 332 void mthca_cleanup_mcg_table(struct mthca_dev *dev) 333 { 334 mthca_alloc_cleanup(&dev->mcg_table.alloc); 335 } 336