1219820Sjeff/* 2219820Sjeff * Copyright (c) 2004 Topspin Communications. All rights reserved. 3219820Sjeff * Copyright (c) 2005 Intel Corporation. All rights reserved. 4219820Sjeff * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. 5219820Sjeff * Copyright (c) 2005 Voltaire, Inc. All rights reserved. 6219820Sjeff * 7219820Sjeff * This software is available to you under a choice of one of two 8219820Sjeff * licenses. You may choose to be licensed under the terms of the GNU 9219820Sjeff * General Public License (GPL) Version 2, available from the file 10219820Sjeff * COPYING in the main directory of this source tree, or the 11219820Sjeff * OpenIB.org BSD license below: 12219820Sjeff * 13219820Sjeff * Redistribution and use in source and binary forms, with or 14219820Sjeff * without modification, are permitted provided that the following 15219820Sjeff * conditions are met: 16219820Sjeff * 17219820Sjeff * - Redistributions of source code must retain the above 18219820Sjeff * copyright notice, this list of conditions and the following 19219820Sjeff * disclaimer. 20219820Sjeff * 21219820Sjeff * - Redistributions in binary form must reproduce the above 22219820Sjeff * copyright notice, this list of conditions and the following 23219820Sjeff * disclaimer in the documentation and/or other materials 24219820Sjeff * provided with the distribution. 25219820Sjeff * 26219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 27219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 28219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 29219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 30219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 31219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 32219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33219820Sjeff * SOFTWARE. 34219820Sjeff */ 35219820Sjeff 36219820Sjeff#include <linux/module.h> 37219820Sjeff#include <linux/errno.h> 38219820Sjeff#include <linux/slab.h> 39219820Sjeff#include <linux/workqueue.h> 40219820Sjeff 41219820Sjeff#include <rdma/ib_cache.h> 42219820Sjeff 43219820Sjeff#include "core_priv.h" 44219820Sjeff 45219820Sjeffstruct ib_pkey_cache { 46219820Sjeff int table_len; 47219820Sjeff u16 table[0]; 48219820Sjeff}; 49219820Sjeff 50219820Sjeffstruct ib_gid_cache { 51219820Sjeff int table_len; 52219820Sjeff union ib_gid table[0]; 53219820Sjeff}; 54219820Sjeff 55219820Sjeffstruct ib_update_work { 56219820Sjeff struct work_struct work; 57219820Sjeff struct ib_device *device; 58219820Sjeff u8 port_num; 59219820Sjeff}; 60219820Sjeff 61219820Sjeffstatic inline int start_port(struct ib_device *device) 62219820Sjeff{ 63219820Sjeff return (device->node_type == RDMA_NODE_IB_SWITCH) ? 0 : 1; 64219820Sjeff} 65219820Sjeff 66219820Sjeffstatic inline int end_port(struct ib_device *device) 67219820Sjeff{ 68219820Sjeff return (device->node_type == RDMA_NODE_IB_SWITCH) ? 69219820Sjeff 0 : device->phys_port_cnt; 70219820Sjeff} 71219820Sjeff 72219820Sjeffint ib_get_cached_gid(struct ib_device *device, 73219820Sjeff u8 port_num, 74219820Sjeff int index, 75219820Sjeff union ib_gid *gid) 76219820Sjeff{ 77219820Sjeff struct ib_gid_cache *cache; 78219820Sjeff unsigned long flags; 79219820Sjeff int ret = 0; 80219820Sjeff 81219820Sjeff if (port_num < start_port(device) || port_num > end_port(device)) 82219820Sjeff return -EINVAL; 83219820Sjeff 84219820Sjeff read_lock_irqsave(&device->cache.lock, flags); 85219820Sjeff 86219820Sjeff cache = device->cache.gid_cache[port_num - start_port(device)]; 87219820Sjeff 88219820Sjeff if (index < 0 || index >= cache->table_len) 89219820Sjeff ret = -EINVAL; 90219820Sjeff else 91219820Sjeff *gid = cache->table[index]; 92219820Sjeff 93219820Sjeff read_unlock_irqrestore(&device->cache.lock, flags); 94219820Sjeff 95219820Sjeff return ret; 96219820Sjeff} 97219820SjeffEXPORT_SYMBOL(ib_get_cached_gid); 98219820Sjeff 99219820Sjeffint ib_find_cached_gid(struct ib_device *device, 100219820Sjeff union ib_gid *gid, 101219820Sjeff u8 *port_num, 102219820Sjeff u16 *index) 103219820Sjeff{ 104219820Sjeff struct ib_gid_cache *cache; 105219820Sjeff unsigned long flags; 106219820Sjeff int p, i; 107219820Sjeff int ret = -ENOENT; 108219820Sjeff 109219820Sjeff *port_num = -1; 110219820Sjeff if (index) 111219820Sjeff *index = -1; 112219820Sjeff 113219820Sjeff read_lock_irqsave(&device->cache.lock, flags); 114219820Sjeff 115219820Sjeff for (p = 0; p <= end_port(device) - start_port(device); ++p) { 116219820Sjeff cache = device->cache.gid_cache[p]; 117219820Sjeff for (i = 0; i < cache->table_len; ++i) { 118219820Sjeff if (!memcmp(gid, &cache->table[i], sizeof *gid)) { 119219820Sjeff *port_num = p + start_port(device); 120219820Sjeff if (index) 121219820Sjeff *index = i; 122219820Sjeff ret = 0; 123219820Sjeff goto found; 124219820Sjeff } 125219820Sjeff } 126219820Sjeff } 127219820Sjefffound: 128219820Sjeff read_unlock_irqrestore(&device->cache.lock, flags); 129219820Sjeff 130219820Sjeff return ret; 131219820Sjeff} 132219820SjeffEXPORT_SYMBOL(ib_find_cached_gid); 133219820Sjeff 134219820Sjeffint ib_get_cached_pkey(struct ib_device *device, 135219820Sjeff u8 port_num, 136219820Sjeff int index, 137219820Sjeff u16 *pkey) 138219820Sjeff{ 139219820Sjeff struct ib_pkey_cache *cache; 140219820Sjeff unsigned long flags; 141219820Sjeff int ret = 0; 142219820Sjeff 143219820Sjeff if (port_num < start_port(device) || port_num > end_port(device)) 144219820Sjeff return -EINVAL; 145219820Sjeff 146219820Sjeff read_lock_irqsave(&device->cache.lock, flags); 147219820Sjeff 148219820Sjeff cache = device->cache.pkey_cache[port_num - start_port(device)]; 149219820Sjeff 150219820Sjeff if (index < 0 || index >= cache->table_len) 151219820Sjeff ret = -EINVAL; 152219820Sjeff else 153219820Sjeff *pkey = cache->table[index]; 154219820Sjeff 155219820Sjeff read_unlock_irqrestore(&device->cache.lock, flags); 156219820Sjeff 157219820Sjeff return ret; 158219820Sjeff} 159219820SjeffEXPORT_SYMBOL(ib_get_cached_pkey); 160219820Sjeff 161219820Sjeffint ib_find_cached_pkey(struct ib_device *device, 162219820Sjeff u8 port_num, 163219820Sjeff u16 pkey, 164219820Sjeff u16 *index) 165219820Sjeff{ 166219820Sjeff struct ib_pkey_cache *cache; 167219820Sjeff unsigned long flags; 168219820Sjeff int i; 169219820Sjeff int ret = -ENOENT; 170219820Sjeff 171219820Sjeff if (port_num < start_port(device) || port_num > end_port(device)) 172219820Sjeff return -EINVAL; 173219820Sjeff 174219820Sjeff read_lock_irqsave(&device->cache.lock, flags); 175219820Sjeff 176219820Sjeff cache = device->cache.pkey_cache[port_num - start_port(device)]; 177219820Sjeff 178219820Sjeff *index = -1; 179219820Sjeff 180219820Sjeff for (i = 0; i < cache->table_len; ++i) 181219820Sjeff if ((cache->table[i] & 0x7fff) == (pkey & 0x7fff)) { 182219820Sjeff *index = i; 183219820Sjeff ret = 0; 184219820Sjeff break; 185219820Sjeff } 186219820Sjeff 187219820Sjeff read_unlock_irqrestore(&device->cache.lock, flags); 188219820Sjeff 189219820Sjeff return ret; 190219820Sjeff} 191219820SjeffEXPORT_SYMBOL(ib_find_cached_pkey); 192219820Sjeff 193219820Sjeffint ib_get_cached_lmc(struct ib_device *device, 194219820Sjeff u8 port_num, 195219820Sjeff u8 *lmc) 196219820Sjeff{ 197219820Sjeff unsigned long flags; 198219820Sjeff int ret = 0; 199219820Sjeff 200219820Sjeff if (port_num < start_port(device) || port_num > end_port(device)) 201219820Sjeff return -EINVAL; 202219820Sjeff 203219820Sjeff read_lock_irqsave(&device->cache.lock, flags); 204219820Sjeff *lmc = device->cache.lmc_cache[port_num - start_port(device)]; 205219820Sjeff read_unlock_irqrestore(&device->cache.lock, flags); 206219820Sjeff 207219820Sjeff return ret; 208219820Sjeff} 209219820SjeffEXPORT_SYMBOL(ib_get_cached_lmc); 210219820Sjeff 211219820Sjeffstatic void ib_cache_update(struct ib_device *device, 212219820Sjeff u8 port) 213219820Sjeff{ 214219820Sjeff struct ib_port_attr *tprops = NULL; 215219820Sjeff struct ib_pkey_cache *pkey_cache = NULL, *old_pkey_cache; 216219820Sjeff struct ib_gid_cache *gid_cache = NULL, *old_gid_cache; 217219820Sjeff int i; 218219820Sjeff int ret; 219219820Sjeff 220219820Sjeff tprops = kmalloc(sizeof *tprops, GFP_KERNEL); 221219820Sjeff if (!tprops) 222219820Sjeff return; 223219820Sjeff 224219820Sjeff ret = ib_query_port(device, port, tprops); 225219820Sjeff if (ret) { 226219820Sjeff printk(KERN_WARNING "ib_query_port failed (%d) for %s\n", 227219820Sjeff ret, device->name); 228219820Sjeff goto err; 229219820Sjeff } 230219820Sjeff 231219820Sjeff pkey_cache = kmalloc(sizeof *pkey_cache + tprops->pkey_tbl_len * 232219820Sjeff sizeof *pkey_cache->table, GFP_KERNEL); 233219820Sjeff if (!pkey_cache) 234219820Sjeff goto err; 235219820Sjeff 236219820Sjeff pkey_cache->table_len = tprops->pkey_tbl_len; 237219820Sjeff 238219820Sjeff gid_cache = kmalloc(sizeof *gid_cache + tprops->gid_tbl_len * 239219820Sjeff sizeof *gid_cache->table, GFP_KERNEL); 240219820Sjeff if (!gid_cache) 241219820Sjeff goto err; 242219820Sjeff 243219820Sjeff gid_cache->table_len = tprops->gid_tbl_len; 244219820Sjeff 245219820Sjeff for (i = 0; i < pkey_cache->table_len; ++i) { 246219820Sjeff ret = ib_query_pkey(device, port, i, pkey_cache->table + i); 247219820Sjeff if (ret) { 248219820Sjeff printk(KERN_WARNING "ib_query_pkey failed (%d) for %s (index %d)\n", 249219820Sjeff ret, device->name, i); 250219820Sjeff goto err; 251219820Sjeff } 252219820Sjeff } 253219820Sjeff 254219820Sjeff for (i = 0; i < gid_cache->table_len; ++i) { 255219820Sjeff ret = ib_query_gid(device, port, i, gid_cache->table + i); 256219820Sjeff if (ret) { 257219820Sjeff printk(KERN_WARNING "ib_query_gid failed (%d) for %s (index %d)\n", 258219820Sjeff ret, device->name, i); 259219820Sjeff goto err; 260219820Sjeff } 261219820Sjeff } 262219820Sjeff 263219820Sjeff write_lock_irq(&device->cache.lock); 264219820Sjeff 265219820Sjeff old_pkey_cache = device->cache.pkey_cache[port - start_port(device)]; 266219820Sjeff old_gid_cache = device->cache.gid_cache [port - start_port(device)]; 267219820Sjeff 268219820Sjeff device->cache.pkey_cache[port - start_port(device)] = pkey_cache; 269219820Sjeff device->cache.gid_cache [port - start_port(device)] = gid_cache; 270219820Sjeff 271219820Sjeff device->cache.lmc_cache[port - start_port(device)] = tprops->lmc; 272219820Sjeff 273219820Sjeff write_unlock_irq(&device->cache.lock); 274219820Sjeff 275219820Sjeff kfree(old_pkey_cache); 276219820Sjeff kfree(old_gid_cache); 277219820Sjeff kfree(tprops); 278219820Sjeff return; 279219820Sjeff 280219820Sjefferr: 281219820Sjeff kfree(pkey_cache); 282219820Sjeff kfree(gid_cache); 283219820Sjeff kfree(tprops); 284219820Sjeff} 285219820Sjeff 286219820Sjeffstatic void ib_cache_task(struct work_struct *_work) 287219820Sjeff{ 288219820Sjeff struct ib_update_work *work = 289219820Sjeff container_of(_work, struct ib_update_work, work); 290219820Sjeff 291219820Sjeff ib_cache_update(work->device, work->port_num); 292219820Sjeff kfree(work); 293219820Sjeff} 294219820Sjeff 295219820Sjeffstatic void ib_cache_event(struct ib_event_handler *handler, 296219820Sjeff struct ib_event *event) 297219820Sjeff{ 298219820Sjeff struct ib_update_work *work; 299219820Sjeff 300219820Sjeff if (event->event == IB_EVENT_PORT_ERR || 301219820Sjeff event->event == IB_EVENT_PORT_ACTIVE || 302219820Sjeff event->event == IB_EVENT_LID_CHANGE || 303219820Sjeff event->event == IB_EVENT_PKEY_CHANGE || 304219820Sjeff event->event == IB_EVENT_SM_CHANGE || 305219820Sjeff event->event == IB_EVENT_CLIENT_REREGISTER || 306219820Sjeff event->event == IB_EVENT_GID_CHANGE) { 307219820Sjeff work = kmalloc(sizeof *work, GFP_ATOMIC); 308219820Sjeff if (work) { 309219820Sjeff INIT_WORK(&work->work, ib_cache_task); 310219820Sjeff work->device = event->device; 311219820Sjeff work->port_num = event->element.port_num; 312219820Sjeff schedule_work(&work->work); 313219820Sjeff } 314219820Sjeff } 315219820Sjeff} 316219820Sjeff 317219820Sjeffstatic void ib_cache_setup_one(struct ib_device *device) 318219820Sjeff{ 319219820Sjeff int p; 320219820Sjeff 321219820Sjeff rwlock_init(&device->cache.lock); 322219820Sjeff 323219820Sjeff device->cache.pkey_cache = 324219820Sjeff kmalloc(sizeof *device->cache.pkey_cache * 325219820Sjeff (end_port(device) - start_port(device) + 1), GFP_KERNEL); 326219820Sjeff device->cache.gid_cache = 327219820Sjeff kmalloc(sizeof *device->cache.gid_cache * 328219820Sjeff (end_port(device) - start_port(device) + 1), GFP_KERNEL); 329219820Sjeff 330219820Sjeff device->cache.lmc_cache = kmalloc(sizeof *device->cache.lmc_cache * 331219820Sjeff (end_port(device) - 332219820Sjeff start_port(device) + 1), 333219820Sjeff GFP_KERNEL); 334219820Sjeff 335219820Sjeff if (!device->cache.pkey_cache || !device->cache.gid_cache || 336219820Sjeff !device->cache.lmc_cache) { 337219820Sjeff printk(KERN_WARNING "Couldn't allocate cache " 338219820Sjeff "for %s\n", device->name); 339219820Sjeff goto err; 340219820Sjeff } 341219820Sjeff 342219820Sjeff for (p = 0; p <= end_port(device) - start_port(device); ++p) { 343219820Sjeff device->cache.pkey_cache[p] = NULL; 344219820Sjeff device->cache.gid_cache [p] = NULL; 345219820Sjeff ib_cache_update(device, p + start_port(device)); 346219820Sjeff } 347219820Sjeff 348219820Sjeff INIT_IB_EVENT_HANDLER(&device->cache.event_handler, 349219820Sjeff device, ib_cache_event); 350219820Sjeff if (ib_register_event_handler(&device->cache.event_handler)) 351219820Sjeff goto err_cache; 352219820Sjeff 353219820Sjeff return; 354219820Sjeff 355219820Sjefferr_cache: 356219820Sjeff for (p = 0; p <= end_port(device) - start_port(device); ++p) { 357219820Sjeff kfree(device->cache.pkey_cache[p]); 358219820Sjeff kfree(device->cache.gid_cache[p]); 359219820Sjeff } 360219820Sjeff 361219820Sjefferr: 362219820Sjeff kfree(device->cache.pkey_cache); 363219820Sjeff kfree(device->cache.gid_cache); 364219820Sjeff kfree(device->cache.lmc_cache); 365219820Sjeff} 366219820Sjeff 367219820Sjeffstatic void ib_cache_cleanup_one(struct ib_device *device) 368219820Sjeff{ 369219820Sjeff int p; 370219820Sjeff 371219820Sjeff ib_unregister_event_handler(&device->cache.event_handler); 372219820Sjeff flush_scheduled_work(); 373219820Sjeff 374219820Sjeff for (p = 0; p <= end_port(device) - start_port(device); ++p) { 375219820Sjeff kfree(device->cache.pkey_cache[p]); 376219820Sjeff kfree(device->cache.gid_cache[p]); 377219820Sjeff } 378219820Sjeff 379219820Sjeff kfree(device->cache.pkey_cache); 380219820Sjeff kfree(device->cache.gid_cache); 381219820Sjeff kfree(device->cache.lmc_cache); 382219820Sjeff} 383219820Sjeff 384219820Sjeffstatic struct ib_client cache_client = { 385219820Sjeff .name = "cache", 386219820Sjeff .add = ib_cache_setup_one, 387219820Sjeff .remove = ib_cache_cleanup_one 388219820Sjeff}; 389219820Sjeff 390219820Sjeffint __init ib_cache_setup(void) 391219820Sjeff{ 392219820Sjeff return ib_register_client(&cache_client); 393219820Sjeff} 394219820Sjeff 395219820Sjeffvoid __exit ib_cache_cleanup(void) 396219820Sjeff{ 397219820Sjeff ib_unregister_client(&cache_client); 398219820Sjeff} 399