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