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 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 127 int 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 234 int 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 352 int 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 369 void mthca_cleanup_mcg_table(struct mthca_dev *dev) 370 { 371 mthca_alloc_cleanup(&dev->mcg_table.alloc); 372 } 373