1/* 2 * Copyright (c) 2004 Topspin Communications. All rights reserved. 3 * Copyright (c) 2005 Intel Corporation. All rights reserved. 4 * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. 5 * Copyright (c) 2005 Voltaire, Inc. All rights reserved. 6 * 7 * This software is available to you under a choice of one of two 8 * licenses. You may choose to be licensed under the terms of the GNU 9 * General Public License (GPL) Version 2, available from the file 10 * COPYING in the main directory of this source tree, or the 11 * OpenIB.org BSD license below: 12 * 13 * Redistribution and use in source and binary forms, with or 14 * without modification, are permitted provided that the following 15 * conditions are met: 16 * 17 * - Redistributions of source code must retain the above 18 * copyright notice, this list of conditions and the following 19 * disclaimer. 20 * 21 * - Redistributions in binary form must reproduce the above 22 * copyright notice, this list of conditions and the following 23 * disclaimer in the documentation and/or other materials 24 * provided with the distribution. 25 * 26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 28 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 29 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 30 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 31 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 32 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33 * SOFTWARE. 34 */ 35 36#include <linux/module.h> 37#include <linux/errno.h> 38#include <linux/slab.h> 39#include <linux/workqueue.h> 40 41#include <rdma/ib_cache.h> 42 43#include "core_priv.h" 44 45struct ib_pkey_cache { 46 int table_len; 47 u16 table[0]; 48}; 49 50struct ib_gid_cache { 51 int table_len; 52 union ib_gid table[0]; 53}; 54 55struct ib_update_work { 56 struct work_struct work; 57 struct ib_device *device; 58 u8 port_num; 59}; 60 61static inline int start_port(struct ib_device *device) 62{ 63 return (device->node_type == RDMA_NODE_IB_SWITCH) ? 0 : 1; 64} 65 66static inline int end_port(struct ib_device *device) 67{ 68 return (device->node_type == RDMA_NODE_IB_SWITCH) ? 69 0 : device->phys_port_cnt; 70} 71 72int ib_get_cached_gid(struct ib_device *device, 73 u8 port_num, 74 int index, 75 union ib_gid *gid) 76{ 77 struct ib_gid_cache *cache; 78 unsigned long flags; 79 int ret = -EINVAL; 80 81 if (port_num < start_port(device) || port_num > end_port(device)) 82 return -EINVAL; 83 84 read_lock_irqsave(&device->cache.lock, flags); 85 86 if (device->cache.gid_cache) { 87 cache = device->cache.gid_cache[port_num - start_port(device)]; 88 89 if (cache && index >= 0 && index < cache->table_len) { 90 *gid = cache->table[index]; 91 ret = 0; 92 } 93 } 94 95 read_unlock_irqrestore(&device->cache.lock, flags); 96 97 return ret; 98} 99EXPORT_SYMBOL(ib_get_cached_gid); 100 101int ib_find_cached_gid(struct ib_device *device, 102 union ib_gid *gid, 103 u8 *port_num, 104 u16 *index) 105{ 106 struct ib_gid_cache *cache; 107 unsigned long flags; 108 int p, i; 109 int ret = -ENOENT; 110 111 *port_num = -1; 112 if (index) 113 *index = -1; 114 115 read_lock_irqsave(&device->cache.lock, flags); 116 if (!device->cache.gid_cache) 117 goto out; 118 for (p = 0; p <= end_port(device) - start_port(device); ++p) { 119 cache = device->cache.gid_cache[p]; 120 if (!cache) 121 continue; 122 for (i = 0; i < cache->table_len; ++i) { 123 if (!memcmp(gid, &cache->table[i], sizeof *gid)) { 124 *port_num = p + start_port(device); 125 if (index) 126 *index = i; 127 ret = 0; 128 goto out; 129 } 130 } 131 } 132out: 133 read_unlock_irqrestore(&device->cache.lock, flags); 134 return ret; 135} 136EXPORT_SYMBOL(ib_find_cached_gid); 137 138int ib_get_cached_pkey(struct ib_device *device, 139 u8 port_num, 140 int index, 141 u16 *pkey) 142{ 143 struct ib_pkey_cache *cache; 144 unsigned long flags; 145 int ret = -EINVAL; 146 147 if (port_num < start_port(device) || port_num > end_port(device)) 148 return -EINVAL; 149 150 read_lock_irqsave(&device->cache.lock, flags); 151 152 if (device->cache.pkey_cache) { 153 cache = device->cache.pkey_cache[port_num - start_port(device)]; 154 155 if (cache && index >= 0 && index < cache->table_len) { 156 *pkey = cache->table[index]; 157 ret = 0; 158 } 159 } 160 161 read_unlock_irqrestore(&device->cache.lock, flags); 162 163 return ret; 164} 165EXPORT_SYMBOL(ib_get_cached_pkey); 166 167int ib_find_cached_pkey(struct ib_device *device, 168 u8 port_num, 169 u16 pkey, 170 u16 *index) 171{ 172 struct ib_pkey_cache *cache; 173 unsigned long flags; 174 int i; 175 int ret = -ENOENT; 176 int partial_ix = -1; 177 178 if (port_num < start_port(device) || port_num > end_port(device)) 179 return -EINVAL; 180 181 *index = -1; 182 183 read_lock_irqsave(&device->cache.lock, flags); 184 185 if (!device->cache.pkey_cache) 186 goto out; 187 188 cache = device->cache.pkey_cache[port_num - start_port(device)]; 189 if (!cache) 190 goto out; 191 192 for (i = 0; i < cache->table_len; ++i) 193 if ((cache->table[i] & 0x7fff) == (pkey & 0x7fff)) { 194 if (cache->table[i] & 0x8000) { 195 *index = i; 196 ret = 0; 197 break; 198 } else 199 partial_ix = i; 200 } 201 202 if (ret && partial_ix >= 0) { 203 *index = partial_ix; 204 ret = 0; 205 } 206out: 207 read_unlock_irqrestore(&device->cache.lock, flags); 208 return ret; 209} 210EXPORT_SYMBOL(ib_find_cached_pkey); 211 212int ib_find_exact_cached_pkey(struct ib_device *device, 213 u8 port_num, 214 u16 pkey, 215 u16 *index) 216{ 217 struct ib_pkey_cache *cache; 218 unsigned long flags; 219 int i; 220 int ret = -ENOENT; 221 222 if (port_num < start_port(device) || port_num > end_port(device)) 223 return -EINVAL; 224 225 *index = -1; 226 227 read_lock_irqsave(&device->cache.lock, flags); 228 229 if (!device->cache.pkey_cache) 230 goto out; 231 232 cache = device->cache.pkey_cache[port_num - start_port(device)]; 233 if (!cache) 234 goto out; 235 236 for (i = 0; i < cache->table_len; ++i) 237 if (cache->table[i] == pkey) { 238 *index = i; 239 ret = 0; 240 break; 241 } 242out: 243 read_unlock_irqrestore(&device->cache.lock, flags); 244 return ret; 245} 246EXPORT_SYMBOL(ib_find_exact_cached_pkey); 247 248int ib_get_cached_lmc(struct ib_device *device, 249 u8 port_num, 250 u8 *lmc) 251{ 252 unsigned long flags; 253 int ret = -EINVAL; 254 255 if (port_num < start_port(device) || port_num > end_port(device)) 256 return -EINVAL; 257 258 read_lock_irqsave(&device->cache.lock, flags); 259 if (device->cache.lmc_cache) { 260 *lmc = device->cache.lmc_cache[port_num - start_port(device)]; 261 ret = 0; 262 } 263 read_unlock_irqrestore(&device->cache.lock, flags); 264 265 return ret; 266} 267EXPORT_SYMBOL(ib_get_cached_lmc); 268 269static void ib_cache_update(struct ib_device *device, 270 u8 port) 271{ 272 struct ib_port_attr *tprops = NULL; 273 struct ib_pkey_cache *pkey_cache = NULL, *old_pkey_cache; 274 struct ib_gid_cache *gid_cache = NULL, *old_gid_cache; 275 int i; 276 int ret; 277 278 if (!(device->cache.pkey_cache && device->cache.gid_cache && 279 device->cache.lmc_cache)) 280 return; 281 282 tprops = kmalloc(sizeof *tprops, GFP_KERNEL); 283 if (!tprops) 284 return; 285 286 ret = ib_query_port(device, port, tprops); 287 if (ret) { 288 printk(KERN_WARNING "ib_query_port failed (%d) for %s\n", 289 ret, device->name); 290 goto err; 291 } 292 293 pkey_cache = kmalloc(sizeof *pkey_cache + tprops->pkey_tbl_len * 294 sizeof *pkey_cache->table, GFP_KERNEL); 295 if (!pkey_cache) 296 goto err; 297 298 pkey_cache->table_len = tprops->pkey_tbl_len; 299 300 gid_cache = kmalloc(sizeof *gid_cache + tprops->gid_tbl_len * 301 sizeof *gid_cache->table, GFP_KERNEL); 302 if (!gid_cache) 303 goto err; 304 305 gid_cache->table_len = tprops->gid_tbl_len; 306 307 for (i = 0; i < pkey_cache->table_len; ++i) { 308 ret = ib_query_pkey(device, port, i, pkey_cache->table + i); 309 if (ret) { 310 printk(KERN_WARNING "ib_query_pkey failed (%d) for %s (index %d)\n", 311 ret, device->name, i); 312 goto err; 313 } 314 } 315 316 for (i = 0; i < gid_cache->table_len; ++i) { 317 ret = ib_query_gid(device, port, i, gid_cache->table + i); 318 if (ret) { 319 printk(KERN_WARNING "ib_query_gid failed (%d) for %s (index %d)\n", 320 ret, device->name, i); 321 goto err; 322 } 323 } 324 325 write_lock_irq(&device->cache.lock); 326 327 old_pkey_cache = device->cache.pkey_cache[port - start_port(device)]; 328 old_gid_cache = device->cache.gid_cache [port - start_port(device)]; 329 330 device->cache.pkey_cache[port - start_port(device)] = pkey_cache; 331 device->cache.gid_cache [port - start_port(device)] = gid_cache; 332 333 device->cache.lmc_cache[port - start_port(device)] = tprops->lmc; 334 335 write_unlock_irq(&device->cache.lock); 336 337 kfree(old_pkey_cache); 338 kfree(old_gid_cache); 339 kfree(tprops); 340 return; 341 342err: 343 kfree(pkey_cache); 344 kfree(gid_cache); 345 kfree(tprops); 346} 347 348static void ib_cache_task(struct work_struct *_work) 349{ 350 struct ib_update_work *work = 351 container_of(_work, struct ib_update_work, work); 352 353 ib_cache_update(work->device, work->port_num); 354 kfree(work); 355} 356 357static void ib_cache_event(struct ib_event_handler *handler, 358 struct ib_event *event) 359{ 360 struct ib_update_work *work; 361 362 if (event->event == IB_EVENT_PORT_ERR || 363 event->event == IB_EVENT_PORT_ACTIVE || 364 event->event == IB_EVENT_LID_CHANGE || 365 event->event == IB_EVENT_PKEY_CHANGE || 366 event->event == IB_EVENT_SM_CHANGE || 367 event->event == IB_EVENT_CLIENT_REREGISTER || 368 event->event == IB_EVENT_GID_CHANGE) { 369 work = kmalloc(sizeof *work, GFP_ATOMIC); 370 if (work) { 371 INIT_WORK(&work->work, ib_cache_task); 372 work->device = event->device; 373 work->port_num = event->element.port_num; 374 queue_work(ib_wq, &work->work); 375 } 376 } 377} 378 379static void ib_cache_setup_one(struct ib_device *device) 380{ 381 int p; 382 383 rwlock_init(&device->cache.lock); 384 385 device->cache.pkey_cache = 386 kmalloc(sizeof *device->cache.pkey_cache * 387 (end_port(device) - start_port(device) + 1), GFP_KERNEL); 388 device->cache.gid_cache = 389 kmalloc(sizeof *device->cache.gid_cache * 390 (end_port(device) - start_port(device) + 1), GFP_KERNEL); 391 392 device->cache.lmc_cache = kmalloc(sizeof *device->cache.lmc_cache * 393 (end_port(device) - 394 start_port(device) + 1), 395 GFP_KERNEL); 396 397 if (!device->cache.pkey_cache || !device->cache.gid_cache || 398 !device->cache.lmc_cache) { 399 printk(KERN_WARNING "Couldn't allocate cache " 400 "for %s\n", device->name); 401 goto err; 402 } 403 404 for (p = 0; p <= end_port(device) - start_port(device); ++p) { 405 device->cache.pkey_cache[p] = NULL; 406 device->cache.gid_cache [p] = NULL; 407 ib_cache_update(device, p + start_port(device)); 408 } 409 410 INIT_IB_EVENT_HANDLER(&device->cache.event_handler, 411 device, ib_cache_event); 412 if (ib_register_event_handler(&device->cache.event_handler)) 413 goto err_cache; 414 415 return; 416 417err_cache: 418 for (p = 0; p <= end_port(device) - start_port(device); ++p) { 419 kfree(device->cache.pkey_cache[p]); 420 kfree(device->cache.gid_cache[p]); 421 } 422 423err: 424 kfree(device->cache.pkey_cache); 425 kfree(device->cache.gid_cache); 426 kfree(device->cache.lmc_cache); 427 device->cache.pkey_cache = NULL; 428 device->cache.gid_cache = NULL; 429 device->cache.lmc_cache = NULL; 430} 431 432static void ib_cache_cleanup_one(struct ib_device *device) 433{ 434 int p; 435 436 if (!(device->cache.pkey_cache && device->cache.gid_cache && 437 device->cache.lmc_cache)) 438 return; 439 440 ib_unregister_event_handler(&device->cache.event_handler); 441 flush_workqueue(ib_wq); 442 443 for (p = 0; p <= end_port(device) - start_port(device); ++p) { 444 kfree(device->cache.pkey_cache[p]); 445 kfree(device->cache.gid_cache[p]); 446 } 447 448 kfree(device->cache.pkey_cache); 449 kfree(device->cache.gid_cache); 450 kfree(device->cache.lmc_cache); 451} 452 453static struct ib_client cache_client = { 454 .name = "cache", 455 .add = ib_cache_setup_one, 456 .remove = ib_cache_cleanup_one 457}; 458 459int __init ib_cache_setup(void) 460{ 461 return ib_register_client(&cache_client); 462} 463 464void __exit ib_cache_cleanup(void) 465{ 466 ib_unregister_client(&cache_client); 467} 468