diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/infiniband/core/sysfs.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/infiniband/core/sysfs.c')
-rw-r--r-- | drivers/infiniband/core/sysfs.c | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c new file mode 100644 index 000000000000..3a413f72ff6d --- /dev/null +++ b/drivers/infiniband/core/sysfs.c | |||
@@ -0,0 +1,762 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2004, 2005 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: sysfs.c 1349 2004-12-16 21:09:43Z roland $ | ||
33 | */ | ||
34 | |||
35 | #include "core_priv.h" | ||
36 | |||
37 | #include <ib_mad.h> | ||
38 | |||
39 | struct ib_port { | ||
40 | struct kobject kobj; | ||
41 | struct ib_device *ibdev; | ||
42 | struct attribute_group gid_group; | ||
43 | struct attribute **gid_attr; | ||
44 | struct attribute_group pkey_group; | ||
45 | struct attribute **pkey_attr; | ||
46 | u8 port_num; | ||
47 | }; | ||
48 | |||
49 | struct port_attribute { | ||
50 | struct attribute attr; | ||
51 | ssize_t (*show)(struct ib_port *, struct port_attribute *, char *buf); | ||
52 | ssize_t (*store)(struct ib_port *, struct port_attribute *, | ||
53 | const char *buf, size_t count); | ||
54 | }; | ||
55 | |||
56 | #define PORT_ATTR(_name, _mode, _show, _store) \ | ||
57 | struct port_attribute port_attr_##_name = __ATTR(_name, _mode, _show, _store) | ||
58 | |||
59 | #define PORT_ATTR_RO(_name) \ | ||
60 | struct port_attribute port_attr_##_name = __ATTR_RO(_name) | ||
61 | |||
62 | struct port_table_attribute { | ||
63 | struct port_attribute attr; | ||
64 | int index; | ||
65 | }; | ||
66 | |||
67 | static ssize_t port_attr_show(struct kobject *kobj, | ||
68 | struct attribute *attr, char *buf) | ||
69 | { | ||
70 | struct port_attribute *port_attr = | ||
71 | container_of(attr, struct port_attribute, attr); | ||
72 | struct ib_port *p = container_of(kobj, struct ib_port, kobj); | ||
73 | |||
74 | if (!port_attr->show) | ||
75 | return 0; | ||
76 | |||
77 | return port_attr->show(p, port_attr, buf); | ||
78 | } | ||
79 | |||
80 | static struct sysfs_ops port_sysfs_ops = { | ||
81 | .show = port_attr_show | ||
82 | }; | ||
83 | |||
84 | static ssize_t state_show(struct ib_port *p, struct port_attribute *unused, | ||
85 | char *buf) | ||
86 | { | ||
87 | struct ib_port_attr attr; | ||
88 | ssize_t ret; | ||
89 | |||
90 | static const char *state_name[] = { | ||
91 | [IB_PORT_NOP] = "NOP", | ||
92 | [IB_PORT_DOWN] = "DOWN", | ||
93 | [IB_PORT_INIT] = "INIT", | ||
94 | [IB_PORT_ARMED] = "ARMED", | ||
95 | [IB_PORT_ACTIVE] = "ACTIVE", | ||
96 | [IB_PORT_ACTIVE_DEFER] = "ACTIVE_DEFER" | ||
97 | }; | ||
98 | |||
99 | ret = ib_query_port(p->ibdev, p->port_num, &attr); | ||
100 | if (ret) | ||
101 | return ret; | ||
102 | |||
103 | return sprintf(buf, "%d: %s\n", attr.state, | ||
104 | attr.state >= 0 && attr.state <= ARRAY_SIZE(state_name) ? | ||
105 | state_name[attr.state] : "UNKNOWN"); | ||
106 | } | ||
107 | |||
108 | static ssize_t lid_show(struct ib_port *p, struct port_attribute *unused, | ||
109 | char *buf) | ||
110 | { | ||
111 | struct ib_port_attr attr; | ||
112 | ssize_t ret; | ||
113 | |||
114 | ret = ib_query_port(p->ibdev, p->port_num, &attr); | ||
115 | if (ret) | ||
116 | return ret; | ||
117 | |||
118 | return sprintf(buf, "0x%x\n", attr.lid); | ||
119 | } | ||
120 | |||
121 | static ssize_t lid_mask_count_show(struct ib_port *p, | ||
122 | struct port_attribute *unused, | ||
123 | char *buf) | ||
124 | { | ||
125 | struct ib_port_attr attr; | ||
126 | ssize_t ret; | ||
127 | |||
128 | ret = ib_query_port(p->ibdev, p->port_num, &attr); | ||
129 | if (ret) | ||
130 | return ret; | ||
131 | |||
132 | return sprintf(buf, "%d\n", attr.lmc); | ||
133 | } | ||
134 | |||
135 | static ssize_t sm_lid_show(struct ib_port *p, struct port_attribute *unused, | ||
136 | char *buf) | ||
137 | { | ||
138 | struct ib_port_attr attr; | ||
139 | ssize_t ret; | ||
140 | |||
141 | ret = ib_query_port(p->ibdev, p->port_num, &attr); | ||
142 | if (ret) | ||
143 | return ret; | ||
144 | |||
145 | return sprintf(buf, "0x%x\n", attr.sm_lid); | ||
146 | } | ||
147 | |||
148 | static ssize_t sm_sl_show(struct ib_port *p, struct port_attribute *unused, | ||
149 | char *buf) | ||
150 | { | ||
151 | struct ib_port_attr attr; | ||
152 | ssize_t ret; | ||
153 | |||
154 | ret = ib_query_port(p->ibdev, p->port_num, &attr); | ||
155 | if (ret) | ||
156 | return ret; | ||
157 | |||
158 | return sprintf(buf, "%d\n", attr.sm_sl); | ||
159 | } | ||
160 | |||
161 | static ssize_t cap_mask_show(struct ib_port *p, struct port_attribute *unused, | ||
162 | char *buf) | ||
163 | { | ||
164 | struct ib_port_attr attr; | ||
165 | ssize_t ret; | ||
166 | |||
167 | ret = ib_query_port(p->ibdev, p->port_num, &attr); | ||
168 | if (ret) | ||
169 | return ret; | ||
170 | |||
171 | return sprintf(buf, "0x%08x\n", attr.port_cap_flags); | ||
172 | } | ||
173 | |||
174 | static ssize_t rate_show(struct ib_port *p, struct port_attribute *unused, | ||
175 | char *buf) | ||
176 | { | ||
177 | struct ib_port_attr attr; | ||
178 | char *speed = ""; | ||
179 | int rate; | ||
180 | ssize_t ret; | ||
181 | |||
182 | ret = ib_query_port(p->ibdev, p->port_num, &attr); | ||
183 | if (ret) | ||
184 | return ret; | ||
185 | |||
186 | switch (attr.active_speed) { | ||
187 | case 2: speed = " DDR"; break; | ||
188 | case 4: speed = " QDR"; break; | ||
189 | } | ||
190 | |||
191 | rate = 25 * ib_width_enum_to_int(attr.active_width) * attr.active_speed; | ||
192 | if (rate < 0) | ||
193 | return -EINVAL; | ||
194 | |||
195 | return sprintf(buf, "%d%s Gb/sec (%dX%s)\n", | ||
196 | rate / 10, rate % 10 ? ".5" : "", | ||
197 | ib_width_enum_to_int(attr.active_width), speed); | ||
198 | } | ||
199 | |||
200 | static ssize_t phys_state_show(struct ib_port *p, struct port_attribute *unused, | ||
201 | char *buf) | ||
202 | { | ||
203 | struct ib_port_attr attr; | ||
204 | |||
205 | ssize_t ret; | ||
206 | |||
207 | ret = ib_query_port(p->ibdev, p->port_num, &attr); | ||
208 | if (ret) | ||
209 | return ret; | ||
210 | |||
211 | switch (attr.phys_state) { | ||
212 | case 1: return sprintf(buf, "1: Sleep\n"); | ||
213 | case 2: return sprintf(buf, "2: Polling\n"); | ||
214 | case 3: return sprintf(buf, "3: Disabled\n"); | ||
215 | case 4: return sprintf(buf, "4: PortConfigurationTraining\n"); | ||
216 | case 5: return sprintf(buf, "5: LinkUp\n"); | ||
217 | case 6: return sprintf(buf, "6: LinkErrorRecovery\n"); | ||
218 | case 7: return sprintf(buf, "7: Phy Test\n"); | ||
219 | default: return sprintf(buf, "%d: <unknown>\n", attr.phys_state); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | static PORT_ATTR_RO(state); | ||
224 | static PORT_ATTR_RO(lid); | ||
225 | static PORT_ATTR_RO(lid_mask_count); | ||
226 | static PORT_ATTR_RO(sm_lid); | ||
227 | static PORT_ATTR_RO(sm_sl); | ||
228 | static PORT_ATTR_RO(cap_mask); | ||
229 | static PORT_ATTR_RO(rate); | ||
230 | static PORT_ATTR_RO(phys_state); | ||
231 | |||
232 | static struct attribute *port_default_attrs[] = { | ||
233 | &port_attr_state.attr, | ||
234 | &port_attr_lid.attr, | ||
235 | &port_attr_lid_mask_count.attr, | ||
236 | &port_attr_sm_lid.attr, | ||
237 | &port_attr_sm_sl.attr, | ||
238 | &port_attr_cap_mask.attr, | ||
239 | &port_attr_rate.attr, | ||
240 | &port_attr_phys_state.attr, | ||
241 | NULL | ||
242 | }; | ||
243 | |||
244 | static ssize_t show_port_gid(struct ib_port *p, struct port_attribute *attr, | ||
245 | char *buf) | ||
246 | { | ||
247 | struct port_table_attribute *tab_attr = | ||
248 | container_of(attr, struct port_table_attribute, attr); | ||
249 | union ib_gid gid; | ||
250 | ssize_t ret; | ||
251 | |||
252 | ret = ib_query_gid(p->ibdev, p->port_num, tab_attr->index, &gid); | ||
253 | if (ret) | ||
254 | return ret; | ||
255 | |||
256 | return sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", | ||
257 | be16_to_cpu(((u16 *) gid.raw)[0]), | ||
258 | be16_to_cpu(((u16 *) gid.raw)[1]), | ||
259 | be16_to_cpu(((u16 *) gid.raw)[2]), | ||
260 | be16_to_cpu(((u16 *) gid.raw)[3]), | ||
261 | be16_to_cpu(((u16 *) gid.raw)[4]), | ||
262 | be16_to_cpu(((u16 *) gid.raw)[5]), | ||
263 | be16_to_cpu(((u16 *) gid.raw)[6]), | ||
264 | be16_to_cpu(((u16 *) gid.raw)[7])); | ||
265 | } | ||
266 | |||
267 | static ssize_t show_port_pkey(struct ib_port *p, struct port_attribute *attr, | ||
268 | char *buf) | ||
269 | { | ||
270 | struct port_table_attribute *tab_attr = | ||
271 | container_of(attr, struct port_table_attribute, attr); | ||
272 | u16 pkey; | ||
273 | ssize_t ret; | ||
274 | |||
275 | ret = ib_query_pkey(p->ibdev, p->port_num, tab_attr->index, &pkey); | ||
276 | if (ret) | ||
277 | return ret; | ||
278 | |||
279 | return sprintf(buf, "0x%04x\n", pkey); | ||
280 | } | ||
281 | |||
282 | #define PORT_PMA_ATTR(_name, _counter, _width, _offset) \ | ||
283 | struct port_table_attribute port_pma_attr_##_name = { \ | ||
284 | .attr = __ATTR(_name, S_IRUGO, show_pma_counter, NULL), \ | ||
285 | .index = (_offset) | ((_width) << 16) | ((_counter) << 24) \ | ||
286 | } | ||
287 | |||
288 | static ssize_t show_pma_counter(struct ib_port *p, struct port_attribute *attr, | ||
289 | char *buf) | ||
290 | { | ||
291 | struct port_table_attribute *tab_attr = | ||
292 | container_of(attr, struct port_table_attribute, attr); | ||
293 | int offset = tab_attr->index & 0xffff; | ||
294 | int width = (tab_attr->index >> 16) & 0xff; | ||
295 | struct ib_mad *in_mad = NULL; | ||
296 | struct ib_mad *out_mad = NULL; | ||
297 | ssize_t ret; | ||
298 | |||
299 | if (!p->ibdev->process_mad) | ||
300 | return sprintf(buf, "N/A (no PMA)\n"); | ||
301 | |||
302 | in_mad = kmalloc(sizeof *in_mad, GFP_KERNEL); | ||
303 | out_mad = kmalloc(sizeof *in_mad, GFP_KERNEL); | ||
304 | if (!in_mad || !out_mad) { | ||
305 | ret = -ENOMEM; | ||
306 | goto out; | ||
307 | } | ||
308 | |||
309 | memset(in_mad, 0, sizeof *in_mad); | ||
310 | in_mad->mad_hdr.base_version = 1; | ||
311 | in_mad->mad_hdr.mgmt_class = IB_MGMT_CLASS_PERF_MGMT; | ||
312 | in_mad->mad_hdr.class_version = 1; | ||
313 | in_mad->mad_hdr.method = IB_MGMT_METHOD_GET; | ||
314 | in_mad->mad_hdr.attr_id = cpu_to_be16(0x12); /* PortCounters */ | ||
315 | |||
316 | in_mad->data[41] = p->port_num; /* PortSelect field */ | ||
317 | |||
318 | if ((p->ibdev->process_mad(p->ibdev, IB_MAD_IGNORE_MKEY, | ||
319 | p->port_num, NULL, NULL, in_mad, out_mad) & | ||
320 | (IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY)) != | ||
321 | (IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY)) { | ||
322 | ret = -EINVAL; | ||
323 | goto out; | ||
324 | } | ||
325 | |||
326 | switch (width) { | ||
327 | case 4: | ||
328 | ret = sprintf(buf, "%u\n", (out_mad->data[40 + offset / 8] >> | ||
329 | (offset % 4)) & 0xf); | ||
330 | break; | ||
331 | case 8: | ||
332 | ret = sprintf(buf, "%u\n", out_mad->data[40 + offset / 8]); | ||
333 | break; | ||
334 | case 16: | ||
335 | ret = sprintf(buf, "%u\n", | ||
336 | be16_to_cpup((u16 *)(out_mad->data + 40 + offset / 8))); | ||
337 | break; | ||
338 | case 32: | ||
339 | ret = sprintf(buf, "%u\n", | ||
340 | be32_to_cpup((u32 *)(out_mad->data + 40 + offset / 8))); | ||
341 | break; | ||
342 | default: | ||
343 | ret = 0; | ||
344 | } | ||
345 | |||
346 | out: | ||
347 | kfree(in_mad); | ||
348 | kfree(out_mad); | ||
349 | |||
350 | return ret; | ||
351 | } | ||
352 | |||
353 | static PORT_PMA_ATTR(symbol_error , 0, 16, 32); | ||
354 | static PORT_PMA_ATTR(link_error_recovery , 1, 8, 48); | ||
355 | static PORT_PMA_ATTR(link_downed , 2, 8, 56); | ||
356 | static PORT_PMA_ATTR(port_rcv_errors , 3, 16, 64); | ||
357 | static PORT_PMA_ATTR(port_rcv_remote_physical_errors, 4, 16, 80); | ||
358 | static PORT_PMA_ATTR(port_rcv_switch_relay_errors , 5, 16, 96); | ||
359 | static PORT_PMA_ATTR(port_xmit_discards , 6, 16, 112); | ||
360 | static PORT_PMA_ATTR(port_xmit_constraint_errors , 7, 8, 128); | ||
361 | static PORT_PMA_ATTR(port_rcv_constraint_errors , 8, 8, 136); | ||
362 | static PORT_PMA_ATTR(local_link_integrity_errors , 9, 4, 152); | ||
363 | static PORT_PMA_ATTR(excessive_buffer_overrun_errors, 10, 4, 156); | ||
364 | static PORT_PMA_ATTR(VL15_dropped , 11, 16, 176); | ||
365 | static PORT_PMA_ATTR(port_xmit_data , 12, 32, 192); | ||
366 | static PORT_PMA_ATTR(port_rcv_data , 13, 32, 224); | ||
367 | static PORT_PMA_ATTR(port_xmit_packets , 14, 32, 256); | ||
368 | static PORT_PMA_ATTR(port_rcv_packets , 15, 32, 288); | ||
369 | |||
370 | static struct attribute *pma_attrs[] = { | ||
371 | &port_pma_attr_symbol_error.attr.attr, | ||
372 | &port_pma_attr_link_error_recovery.attr.attr, | ||
373 | &port_pma_attr_link_downed.attr.attr, | ||
374 | &port_pma_attr_port_rcv_errors.attr.attr, | ||
375 | &port_pma_attr_port_rcv_remote_physical_errors.attr.attr, | ||
376 | &port_pma_attr_port_rcv_switch_relay_errors.attr.attr, | ||
377 | &port_pma_attr_port_xmit_discards.attr.attr, | ||
378 | &port_pma_attr_port_xmit_constraint_errors.attr.attr, | ||
379 | &port_pma_attr_port_rcv_constraint_errors.attr.attr, | ||
380 | &port_pma_attr_local_link_integrity_errors.attr.attr, | ||
381 | &port_pma_attr_excessive_buffer_overrun_errors.attr.attr, | ||
382 | &port_pma_attr_VL15_dropped.attr.attr, | ||
383 | &port_pma_attr_port_xmit_data.attr.attr, | ||
384 | &port_pma_attr_port_rcv_data.attr.attr, | ||
385 | &port_pma_attr_port_xmit_packets.attr.attr, | ||
386 | &port_pma_attr_port_rcv_packets.attr.attr, | ||
387 | NULL | ||
388 | }; | ||
389 | |||
390 | static struct attribute_group pma_group = { | ||
391 | .name = "counters", | ||
392 | .attrs = pma_attrs | ||
393 | }; | ||
394 | |||
395 | static void ib_port_release(struct kobject *kobj) | ||
396 | { | ||
397 | struct ib_port *p = container_of(kobj, struct ib_port, kobj); | ||
398 | struct attribute *a; | ||
399 | int i; | ||
400 | |||
401 | for (i = 0; (a = p->gid_attr[i]); ++i) { | ||
402 | kfree(a->name); | ||
403 | kfree(a); | ||
404 | } | ||
405 | |||
406 | for (i = 0; (a = p->pkey_attr[i]); ++i) { | ||
407 | kfree(a->name); | ||
408 | kfree(a); | ||
409 | } | ||
410 | |||
411 | kfree(p->gid_attr); | ||
412 | kfree(p); | ||
413 | } | ||
414 | |||
415 | static struct kobj_type port_type = { | ||
416 | .release = ib_port_release, | ||
417 | .sysfs_ops = &port_sysfs_ops, | ||
418 | .default_attrs = port_default_attrs | ||
419 | }; | ||
420 | |||
421 | static void ib_device_release(struct class_device *cdev) | ||
422 | { | ||
423 | struct ib_device *dev = container_of(cdev, struct ib_device, class_dev); | ||
424 | |||
425 | kfree(dev); | ||
426 | } | ||
427 | |||
428 | static int ib_device_hotplug(struct class_device *cdev, char **envp, | ||
429 | int num_envp, char *buf, int size) | ||
430 | { | ||
431 | struct ib_device *dev = container_of(cdev, struct ib_device, class_dev); | ||
432 | int i = 0, len = 0; | ||
433 | |||
434 | if (add_hotplug_env_var(envp, num_envp, &i, buf, size, &len, | ||
435 | "NAME=%s", dev->name)) | ||
436 | return -ENOMEM; | ||
437 | |||
438 | /* | ||
439 | * It might be nice to pass the node GUID to hotplug, but | ||
440 | * right now the only way to get it is to query the device | ||
441 | * provider, and this can crash during device removal because | ||
442 | * we are will be running after driver removal has started. | ||
443 | * We could add a node_guid field to struct ib_device, or we | ||
444 | * could just let the hotplug script read the node GUID from | ||
445 | * sysfs when devices are added. | ||
446 | */ | ||
447 | |||
448 | envp[i] = NULL; | ||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static int alloc_group(struct attribute ***attr, | ||
453 | ssize_t (*show)(struct ib_port *, | ||
454 | struct port_attribute *, char *buf), | ||
455 | int len) | ||
456 | { | ||
457 | struct port_table_attribute ***tab_attr = | ||
458 | (struct port_table_attribute ***) attr; | ||
459 | int i; | ||
460 | int ret; | ||
461 | |||
462 | *tab_attr = kmalloc((1 + len) * sizeof *tab_attr, GFP_KERNEL); | ||
463 | if (!*tab_attr) | ||
464 | return -ENOMEM; | ||
465 | |||
466 | memset(*tab_attr, 0, (1 + len) * sizeof *tab_attr); | ||
467 | |||
468 | for (i = 0; i < len; ++i) { | ||
469 | (*tab_attr)[i] = kmalloc(sizeof *(*tab_attr)[i], GFP_KERNEL); | ||
470 | if (!(*tab_attr)[i]) { | ||
471 | ret = -ENOMEM; | ||
472 | goto err; | ||
473 | } | ||
474 | memset((*tab_attr)[i], 0, sizeof *(*tab_attr)[i]); | ||
475 | (*tab_attr)[i]->attr.attr.name = kmalloc(8, GFP_KERNEL); | ||
476 | if (!(*tab_attr)[i]->attr.attr.name) { | ||
477 | ret = -ENOMEM; | ||
478 | goto err; | ||
479 | } | ||
480 | |||
481 | if (snprintf((*tab_attr)[i]->attr.attr.name, 8, "%d", i) >= 8) { | ||
482 | ret = -ENOMEM; | ||
483 | goto err; | ||
484 | } | ||
485 | |||
486 | (*tab_attr)[i]->attr.attr.mode = S_IRUGO; | ||
487 | (*tab_attr)[i]->attr.attr.owner = THIS_MODULE; | ||
488 | (*tab_attr)[i]->attr.show = show; | ||
489 | (*tab_attr)[i]->index = i; | ||
490 | } | ||
491 | |||
492 | return 0; | ||
493 | |||
494 | err: | ||
495 | for (i = 0; i < len; ++i) { | ||
496 | if ((*tab_attr)[i]) | ||
497 | kfree((*tab_attr)[i]->attr.attr.name); | ||
498 | kfree((*tab_attr)[i]); | ||
499 | } | ||
500 | |||
501 | kfree(*tab_attr); | ||
502 | |||
503 | return ret; | ||
504 | } | ||
505 | |||
506 | static int add_port(struct ib_device *device, int port_num) | ||
507 | { | ||
508 | struct ib_port *p; | ||
509 | struct ib_port_attr attr; | ||
510 | int i; | ||
511 | int ret; | ||
512 | |||
513 | ret = ib_query_port(device, port_num, &attr); | ||
514 | if (ret) | ||
515 | return ret; | ||
516 | |||
517 | p = kmalloc(sizeof *p, GFP_KERNEL); | ||
518 | if (!p) | ||
519 | return -ENOMEM; | ||
520 | memset(p, 0, sizeof *p); | ||
521 | |||
522 | p->ibdev = device; | ||
523 | p->port_num = port_num; | ||
524 | p->kobj.ktype = &port_type; | ||
525 | |||
526 | p->kobj.parent = kobject_get(&device->ports_parent); | ||
527 | if (!p->kobj.parent) { | ||
528 | ret = -EBUSY; | ||
529 | goto err; | ||
530 | } | ||
531 | |||
532 | ret = kobject_set_name(&p->kobj, "%d", port_num); | ||
533 | if (ret) | ||
534 | goto err_put; | ||
535 | |||
536 | ret = kobject_register(&p->kobj); | ||
537 | if (ret) | ||
538 | goto err_put; | ||
539 | |||
540 | ret = sysfs_create_group(&p->kobj, &pma_group); | ||
541 | if (ret) | ||
542 | goto err_put; | ||
543 | |||
544 | ret = alloc_group(&p->gid_attr, show_port_gid, attr.gid_tbl_len); | ||
545 | if (ret) | ||
546 | goto err_remove_pma; | ||
547 | |||
548 | p->gid_group.name = "gids"; | ||
549 | p->gid_group.attrs = p->gid_attr; | ||
550 | |||
551 | ret = sysfs_create_group(&p->kobj, &p->gid_group); | ||
552 | if (ret) | ||
553 | goto err_free_gid; | ||
554 | |||
555 | ret = alloc_group(&p->pkey_attr, show_port_pkey, attr.pkey_tbl_len); | ||
556 | if (ret) | ||
557 | goto err_remove_gid; | ||
558 | |||
559 | p->pkey_group.name = "pkeys"; | ||
560 | p->pkey_group.attrs = p->pkey_attr; | ||
561 | |||
562 | ret = sysfs_create_group(&p->kobj, &p->pkey_group); | ||
563 | if (ret) | ||
564 | goto err_free_pkey; | ||
565 | |||
566 | list_add_tail(&p->kobj.entry, &device->port_list); | ||
567 | |||
568 | return 0; | ||
569 | |||
570 | err_free_pkey: | ||
571 | for (i = 0; i < attr.pkey_tbl_len; ++i) { | ||
572 | kfree(p->pkey_attr[i]->name); | ||
573 | kfree(p->pkey_attr[i]); | ||
574 | } | ||
575 | |||
576 | kfree(p->pkey_attr); | ||
577 | |||
578 | err_remove_gid: | ||
579 | sysfs_remove_group(&p->kobj, &p->gid_group); | ||
580 | |||
581 | err_free_gid: | ||
582 | for (i = 0; i < attr.gid_tbl_len; ++i) { | ||
583 | kfree(p->gid_attr[i]->name); | ||
584 | kfree(p->gid_attr[i]); | ||
585 | } | ||
586 | |||
587 | kfree(p->gid_attr); | ||
588 | |||
589 | err_remove_pma: | ||
590 | sysfs_remove_group(&p->kobj, &pma_group); | ||
591 | |||
592 | err_put: | ||
593 | kobject_put(&device->ports_parent); | ||
594 | |||
595 | err: | ||
596 | kfree(p); | ||
597 | return ret; | ||
598 | } | ||
599 | |||
600 | static ssize_t show_node_type(struct class_device *cdev, char *buf) | ||
601 | { | ||
602 | struct ib_device *dev = container_of(cdev, struct ib_device, class_dev); | ||
603 | |||
604 | switch (dev->node_type) { | ||
605 | case IB_NODE_CA: return sprintf(buf, "%d: CA\n", dev->node_type); | ||
606 | case IB_NODE_SWITCH: return sprintf(buf, "%d: switch\n", dev->node_type); | ||
607 | case IB_NODE_ROUTER: return sprintf(buf, "%d: router\n", dev->node_type); | ||
608 | default: return sprintf(buf, "%d: <unknown>\n", dev->node_type); | ||
609 | } | ||
610 | } | ||
611 | |||
612 | static ssize_t show_sys_image_guid(struct class_device *cdev, char *buf) | ||
613 | { | ||
614 | struct ib_device *dev = container_of(cdev, struct ib_device, class_dev); | ||
615 | struct ib_device_attr attr; | ||
616 | ssize_t ret; | ||
617 | |||
618 | ret = ib_query_device(dev, &attr); | ||
619 | if (ret) | ||
620 | return ret; | ||
621 | |||
622 | return sprintf(buf, "%04x:%04x:%04x:%04x\n", | ||
623 | be16_to_cpu(((u16 *) &attr.sys_image_guid)[0]), | ||
624 | be16_to_cpu(((u16 *) &attr.sys_image_guid)[1]), | ||
625 | be16_to_cpu(((u16 *) &attr.sys_image_guid)[2]), | ||
626 | be16_to_cpu(((u16 *) &attr.sys_image_guid)[3])); | ||
627 | } | ||
628 | |||
629 | static ssize_t show_node_guid(struct class_device *cdev, char *buf) | ||
630 | { | ||
631 | struct ib_device *dev = container_of(cdev, struct ib_device, class_dev); | ||
632 | struct ib_device_attr attr; | ||
633 | ssize_t ret; | ||
634 | |||
635 | ret = ib_query_device(dev, &attr); | ||
636 | if (ret) | ||
637 | return ret; | ||
638 | |||
639 | return sprintf(buf, "%04x:%04x:%04x:%04x\n", | ||
640 | be16_to_cpu(((u16 *) &attr.node_guid)[0]), | ||
641 | be16_to_cpu(((u16 *) &attr.node_guid)[1]), | ||
642 | be16_to_cpu(((u16 *) &attr.node_guid)[2]), | ||
643 | be16_to_cpu(((u16 *) &attr.node_guid)[3])); | ||
644 | } | ||
645 | |||
646 | static CLASS_DEVICE_ATTR(node_type, S_IRUGO, show_node_type, NULL); | ||
647 | static CLASS_DEVICE_ATTR(sys_image_guid, S_IRUGO, show_sys_image_guid, NULL); | ||
648 | static CLASS_DEVICE_ATTR(node_guid, S_IRUGO, show_node_guid, NULL); | ||
649 | |||
650 | static struct class_device_attribute *ib_class_attributes[] = { | ||
651 | &class_device_attr_node_type, | ||
652 | &class_device_attr_sys_image_guid, | ||
653 | &class_device_attr_node_guid | ||
654 | }; | ||
655 | |||
656 | static struct class ib_class = { | ||
657 | .name = "infiniband", | ||
658 | .release = ib_device_release, | ||
659 | .hotplug = ib_device_hotplug, | ||
660 | }; | ||
661 | |||
662 | int ib_device_register_sysfs(struct ib_device *device) | ||
663 | { | ||
664 | struct class_device *class_dev = &device->class_dev; | ||
665 | int ret; | ||
666 | int i; | ||
667 | |||
668 | class_dev->class = &ib_class; | ||
669 | class_dev->class_data = device; | ||
670 | strlcpy(class_dev->class_id, device->name, BUS_ID_SIZE); | ||
671 | |||
672 | INIT_LIST_HEAD(&device->port_list); | ||
673 | |||
674 | ret = class_device_register(class_dev); | ||
675 | if (ret) | ||
676 | goto err; | ||
677 | |||
678 | for (i = 0; i < ARRAY_SIZE(ib_class_attributes); ++i) { | ||
679 | ret = class_device_create_file(class_dev, ib_class_attributes[i]); | ||
680 | if (ret) | ||
681 | goto err_unregister; | ||
682 | } | ||
683 | |||
684 | device->ports_parent.parent = kobject_get(&class_dev->kobj); | ||
685 | if (!device->ports_parent.parent) { | ||
686 | ret = -EBUSY; | ||
687 | goto err_unregister; | ||
688 | } | ||
689 | ret = kobject_set_name(&device->ports_parent, "ports"); | ||
690 | if (ret) | ||
691 | goto err_put; | ||
692 | ret = kobject_register(&device->ports_parent); | ||
693 | if (ret) | ||
694 | goto err_put; | ||
695 | |||
696 | if (device->node_type == IB_NODE_SWITCH) { | ||
697 | ret = add_port(device, 0); | ||
698 | if (ret) | ||
699 | goto err_put; | ||
700 | } else { | ||
701 | int i; | ||
702 | |||
703 | for (i = 1; i <= device->phys_port_cnt; ++i) { | ||
704 | ret = add_port(device, i); | ||
705 | if (ret) | ||
706 | goto err_put; | ||
707 | } | ||
708 | } | ||
709 | |||
710 | return 0; | ||
711 | |||
712 | err_put: | ||
713 | { | ||
714 | struct kobject *p, *t; | ||
715 | struct ib_port *port; | ||
716 | |||
717 | list_for_each_entry_safe(p, t, &device->port_list, entry) { | ||
718 | list_del(&p->entry); | ||
719 | port = container_of(p, struct ib_port, kobj); | ||
720 | sysfs_remove_group(p, &pma_group); | ||
721 | sysfs_remove_group(p, &port->pkey_group); | ||
722 | sysfs_remove_group(p, &port->gid_group); | ||
723 | kobject_unregister(p); | ||
724 | } | ||
725 | } | ||
726 | |||
727 | kobject_put(&class_dev->kobj); | ||
728 | |||
729 | err_unregister: | ||
730 | class_device_unregister(class_dev); | ||
731 | |||
732 | err: | ||
733 | return ret; | ||
734 | } | ||
735 | |||
736 | void ib_device_unregister_sysfs(struct ib_device *device) | ||
737 | { | ||
738 | struct kobject *p, *t; | ||
739 | struct ib_port *port; | ||
740 | |||
741 | list_for_each_entry_safe(p, t, &device->port_list, entry) { | ||
742 | list_del(&p->entry); | ||
743 | port = container_of(p, struct ib_port, kobj); | ||
744 | sysfs_remove_group(p, &pma_group); | ||
745 | sysfs_remove_group(p, &port->pkey_group); | ||
746 | sysfs_remove_group(p, &port->gid_group); | ||
747 | kobject_unregister(p); | ||
748 | } | ||
749 | |||
750 | kobject_unregister(&device->ports_parent); | ||
751 | class_device_unregister(&device->class_dev); | ||
752 | } | ||
753 | |||
754 | int ib_sysfs_setup(void) | ||
755 | { | ||
756 | return class_register(&ib_class); | ||
757 | } | ||
758 | |||
759 | void ib_sysfs_cleanup(void) | ||
760 | { | ||
761 | class_unregister(&ib_class); | ||
762 | } | ||