Linux-2.6.12-rc2
[linux-flexiantxendom0-natty.git] / drivers / infiniband / core / cache.c
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  * $Id: cache.c 1349 2004-12-16 21:09:43Z roland $
33  */
34
35 #include <linux/version.h>
36 #include <linux/module.h>
37 #include <linux/errno.h>
38 #include <linux/slab.h>
39
40 #include <ib_cache.h>
41
42 #include "core_priv.h"
43
44 struct ib_pkey_cache {
45         int             table_len;
46         u16             table[0];
47 };
48
49 struct ib_gid_cache {
50         int             table_len;
51         union ib_gid    table[0];
52 };
53
54 struct ib_update_work {
55         struct work_struct work;
56         struct ib_device  *device;
57         u8                 port_num;
58 };
59
60 static inline int start_port(struct ib_device *device)
61 {
62         return device->node_type == IB_NODE_SWITCH ? 0 : 1;
63 }
64
65 static inline int end_port(struct ib_device *device)
66 {
67         return device->node_type == IB_NODE_SWITCH ? 0 : device->phys_port_cnt;
68 }
69
70 int ib_get_cached_gid(struct ib_device *device,
71                       u8                port_num,
72                       int               index,
73                       union ib_gid     *gid)
74 {
75         struct ib_gid_cache *cache;
76         unsigned long flags;
77         int ret = 0;
78
79         if (port_num < start_port(device) || port_num > end_port(device))
80                 return -EINVAL;
81
82         read_lock_irqsave(&device->cache.lock, flags);
83
84         cache = device->cache.gid_cache[port_num - start_port(device)];
85
86         if (index < 0 || index >= cache->table_len)
87                 ret = -EINVAL;
88         else
89                 *gid = cache->table[index];
90
91         read_unlock_irqrestore(&device->cache.lock, flags);
92
93         return ret;
94 }
95 EXPORT_SYMBOL(ib_get_cached_gid);
96
97 int ib_find_cached_gid(struct ib_device *device,
98                        union ib_gid     *gid,
99                        u8               *port_num,
100                        u16              *index)
101 {
102         struct ib_gid_cache *cache;
103         unsigned long flags;
104         int p, i;
105         int ret = -ENOENT;
106
107         *port_num = -1;
108         if (index)
109                 *index = -1;
110
111         read_lock_irqsave(&device->cache.lock, flags);
112
113         for (p = 0; p <= end_port(device) - start_port(device); ++p) {
114                 cache = device->cache.gid_cache[p];
115                 for (i = 0; i < cache->table_len; ++i) {
116                         if (!memcmp(gid, &cache->table[i], sizeof *gid)) {
117                                 *port_num = p + start_port(device);
118                                 if (index)
119                                         *index = i;
120                                 ret = 0;
121                                 goto found;
122                         }
123                 }
124         }
125 found:
126         read_unlock_irqrestore(&device->cache.lock, flags);
127
128         return ret;
129 }
130 EXPORT_SYMBOL(ib_find_cached_gid);
131
132 int ib_get_cached_pkey(struct ib_device *device,
133                        u8                port_num,
134                        int               index,
135                        u16              *pkey)
136 {
137         struct ib_pkey_cache *cache;
138         unsigned long flags;
139         int ret = 0;
140
141         if (port_num < start_port(device) || port_num > end_port(device))
142                 return -EINVAL;
143
144         read_lock_irqsave(&device->cache.lock, flags);
145
146         cache = device->cache.pkey_cache[port_num - start_port(device)];
147
148         if (index < 0 || index >= cache->table_len)
149                 ret = -EINVAL;
150         else
151                 *pkey = cache->table[index];
152
153         read_unlock_irqrestore(&device->cache.lock, flags);
154
155         return ret;
156 }
157 EXPORT_SYMBOL(ib_get_cached_pkey);
158
159 int ib_find_cached_pkey(struct ib_device *device,
160                         u8                port_num,
161                         u16               pkey,
162                         u16              *index)
163 {
164         struct ib_pkey_cache *cache;
165         unsigned long flags;
166         int i;
167         int ret = -ENOENT;
168
169         if (port_num < start_port(device) || port_num > end_port(device))
170                 return -EINVAL;
171
172         read_lock_irqsave(&device->cache.lock, flags);
173
174         cache = device->cache.pkey_cache[port_num - start_port(device)];
175
176         *index = -1;
177
178         for (i = 0; i < cache->table_len; ++i)
179                 if ((cache->table[i] & 0x7fff) == (pkey & 0x7fff)) {
180                         *index = i;
181                         ret = 0;
182                         break;
183                 }
184
185         read_unlock_irqrestore(&device->cache.lock, flags);
186
187         return ret;
188 }
189 EXPORT_SYMBOL(ib_find_cached_pkey);
190
191 static void ib_cache_update(struct ib_device *device,
192                             u8                port)
193 {
194         struct ib_port_attr       *tprops = NULL;
195         struct ib_pkey_cache      *pkey_cache = NULL, *old_pkey_cache;
196         struct ib_gid_cache       *gid_cache = NULL, *old_gid_cache;
197         int                        i;
198         int                        ret;
199
200         tprops = kmalloc(sizeof *tprops, GFP_KERNEL);
201         if (!tprops)
202                 return;
203
204         ret = ib_query_port(device, port, tprops);
205         if (ret) {
206                 printk(KERN_WARNING "ib_query_port failed (%d) for %s\n",
207                        ret, device->name);
208                 goto err;
209         }
210
211         pkey_cache = kmalloc(sizeof *pkey_cache + tprops->pkey_tbl_len *
212                              sizeof *pkey_cache->table, GFP_KERNEL);
213         if (!pkey_cache)
214                 goto err;
215
216         pkey_cache->table_len = tprops->pkey_tbl_len;
217
218         gid_cache = kmalloc(sizeof *gid_cache + tprops->gid_tbl_len *
219                             sizeof *gid_cache->table, GFP_KERNEL);
220         if (!gid_cache)
221                 goto err;
222
223         gid_cache->table_len = tprops->gid_tbl_len;
224
225         for (i = 0; i < pkey_cache->table_len; ++i) {
226                 ret = ib_query_pkey(device, port, i, pkey_cache->table + i);
227                 if (ret) {
228                         printk(KERN_WARNING "ib_query_pkey failed (%d) for %s (index %d)\n",
229                                ret, device->name, i);
230                         goto err;
231                 }
232         }
233
234         for (i = 0; i < gid_cache->table_len; ++i) {
235                 ret = ib_query_gid(device, port, i, gid_cache->table + i);
236                 if (ret) {
237                         printk(KERN_WARNING "ib_query_gid failed (%d) for %s (index %d)\n",
238                                ret, device->name, i);
239                         goto err;
240                 }
241         }
242
243         write_lock_irq(&device->cache.lock);
244
245         old_pkey_cache = device->cache.pkey_cache[port - start_port(device)];
246         old_gid_cache  = device->cache.gid_cache [port - start_port(device)];
247
248         device->cache.pkey_cache[port - start_port(device)] = pkey_cache;
249         device->cache.gid_cache [port - start_port(device)] = gid_cache;
250
251         write_unlock_irq(&device->cache.lock);
252
253         kfree(old_pkey_cache);
254         kfree(old_gid_cache);
255         kfree(tprops);
256         return;
257
258 err:
259         kfree(pkey_cache);
260         kfree(gid_cache);
261         kfree(tprops);
262 }
263
264 static void ib_cache_task(void *work_ptr)
265 {
266         struct ib_update_work *work = work_ptr;
267
268         ib_cache_update(work->device, work->port_num);
269         kfree(work);
270 }
271
272 static void ib_cache_event(struct ib_event_handler *handler,
273                            struct ib_event *event)
274 {
275         struct ib_update_work *work;
276
277         if (event->event == IB_EVENT_PORT_ERR    ||
278             event->event == IB_EVENT_PORT_ACTIVE ||
279             event->event == IB_EVENT_LID_CHANGE  ||
280             event->event == IB_EVENT_PKEY_CHANGE ||
281             event->event == IB_EVENT_SM_CHANGE) {
282                 work = kmalloc(sizeof *work, GFP_ATOMIC);
283                 if (work) {
284                         INIT_WORK(&work->work, ib_cache_task, work);
285                         work->device   = event->device;
286                         work->port_num = event->element.port_num;
287                         schedule_work(&work->work);
288                 }
289         }
290 }
291
292 static void ib_cache_setup_one(struct ib_device *device)
293 {
294         int p;
295
296         rwlock_init(&device->cache.lock);
297
298         device->cache.pkey_cache =
299                 kmalloc(sizeof *device->cache.pkey_cache *
300                         (end_port(device) - start_port(device) + 1), GFP_KERNEL);
301         device->cache.gid_cache =
302                 kmalloc(sizeof *device->cache.pkey_cache *
303                         (end_port(device) - start_port(device) + 1), GFP_KERNEL);
304
305         if (!device->cache.pkey_cache || !device->cache.gid_cache) {
306                 printk(KERN_WARNING "Couldn't allocate cache "
307                        "for %s\n", device->name);
308                 goto err;
309         }
310
311         for (p = 0; p <= end_port(device) - start_port(device); ++p) {
312                 device->cache.pkey_cache[p] = NULL;
313                 device->cache.gid_cache [p] = NULL;
314                 ib_cache_update(device, p + start_port(device));
315         }
316
317         INIT_IB_EVENT_HANDLER(&device->cache.event_handler,
318                               device, ib_cache_event);
319         if (ib_register_event_handler(&device->cache.event_handler))
320                 goto err_cache;
321
322         return;
323
324 err_cache:
325         for (p = 0; p <= end_port(device) - start_port(device); ++p) {
326                 kfree(device->cache.pkey_cache[p]);
327                 kfree(device->cache.gid_cache[p]);
328         }
329
330 err:
331         kfree(device->cache.pkey_cache);
332         kfree(device->cache.gid_cache);
333 }
334
335 static void ib_cache_cleanup_one(struct ib_device *device)
336 {
337         int p;
338
339         ib_unregister_event_handler(&device->cache.event_handler);
340         flush_scheduled_work();
341
342         for (p = 0; p <= end_port(device) - start_port(device); ++p) {
343                 kfree(device->cache.pkey_cache[p]);
344                 kfree(device->cache.gid_cache[p]);
345         }
346
347         kfree(device->cache.pkey_cache);
348         kfree(device->cache.gid_cache);
349 }
350
351 static struct ib_client cache_client = {
352         .name   = "cache",
353         .add    = ib_cache_setup_one,
354         .remove = ib_cache_cleanup_one
355 };
356
357 int __init ib_cache_setup(void)
358 {
359         return ib_register_client(&cache_client);
360 }
361
362 void __exit ib_cache_cleanup(void)
363 {
364         ib_unregister_client(&cache_client);
365 }