diff options
Diffstat (limited to 'drivers/infiniband/hw/mlx4/sysfs.c')
-rw-r--r-- | drivers/infiniband/hw/mlx4/sysfs.c | 794 |
1 files changed, 794 insertions, 0 deletions
diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c new file mode 100644 index 000000000000..5b2a01dfb907 --- /dev/null +++ b/drivers/infiniband/hw/mlx4/sysfs.c | |||
@@ -0,0 +1,794 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Mellanox Technologies. 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 | |||
33 | /*#include "core_priv.h"*/ | ||
34 | #include "mlx4_ib.h" | ||
35 | #include <linux/slab.h> | ||
36 | #include <linux/string.h> | ||
37 | #include <linux/stat.h> | ||
38 | |||
39 | #include <rdma/ib_mad.h> | ||
40 | /*show_admin_alias_guid returns the administratively assigned value of that GUID. | ||
41 | * Values returned in buf parameter string: | ||
42 | * 0 - requests opensm to assign a value. | ||
43 | * ffffffffffffffff - delete this entry. | ||
44 | * other - value assigned by administrator. | ||
45 | */ | ||
46 | static ssize_t show_admin_alias_guid(struct device *dev, | ||
47 | struct device_attribute *attr, char *buf) | ||
48 | { | ||
49 | int record_num;/*0-15*/ | ||
50 | int guid_index_in_rec; /*0 - 7*/ | ||
51 | struct mlx4_ib_iov_sysfs_attr *mlx4_ib_iov_dentry = | ||
52 | container_of(attr, struct mlx4_ib_iov_sysfs_attr, dentry); | ||
53 | struct mlx4_ib_iov_port *port = mlx4_ib_iov_dentry->ctx; | ||
54 | struct mlx4_ib_dev *mdev = port->dev; | ||
55 | |||
56 | record_num = mlx4_ib_iov_dentry->entry_num / 8 ; | ||
57 | guid_index_in_rec = mlx4_ib_iov_dentry->entry_num % 8 ; | ||
58 | |||
59 | return sprintf(buf, "%llx\n", | ||
60 | be64_to_cpu(*(__be64 *)&mdev->sriov.alias_guid. | ||
61 | ports_guid[port->num - 1]. | ||
62 | all_rec_per_port[record_num]. | ||
63 | all_recs[8 * guid_index_in_rec])); | ||
64 | } | ||
65 | |||
66 | /* store_admin_alias_guid stores the (new) administratively assigned value of that GUID. | ||
67 | * Values in buf parameter string: | ||
68 | * 0 - requests opensm to assign a value. | ||
69 | * 0xffffffffffffffff - delete this entry. | ||
70 | * other - guid value assigned by the administrator. | ||
71 | */ | ||
72 | static ssize_t store_admin_alias_guid(struct device *dev, | ||
73 | struct device_attribute *attr, | ||
74 | const char *buf, size_t count) | ||
75 | { | ||
76 | int record_num;/*0-15*/ | ||
77 | int guid_index_in_rec; /*0 - 7*/ | ||
78 | struct mlx4_ib_iov_sysfs_attr *mlx4_ib_iov_dentry = | ||
79 | container_of(attr, struct mlx4_ib_iov_sysfs_attr, dentry); | ||
80 | struct mlx4_ib_iov_port *port = mlx4_ib_iov_dentry->ctx; | ||
81 | struct mlx4_ib_dev *mdev = port->dev; | ||
82 | u64 sysadmin_ag_val; | ||
83 | |||
84 | record_num = mlx4_ib_iov_dentry->entry_num / 8; | ||
85 | guid_index_in_rec = mlx4_ib_iov_dentry->entry_num % 8; | ||
86 | if (0 == record_num && 0 == guid_index_in_rec) { | ||
87 | pr_err("GUID 0 block 0 is RO\n"); | ||
88 | return count; | ||
89 | } | ||
90 | sscanf(buf, "%llx", &sysadmin_ag_val); | ||
91 | *(__be64 *)&mdev->sriov.alias_guid.ports_guid[port->num - 1]. | ||
92 | all_rec_per_port[record_num]. | ||
93 | all_recs[GUID_REC_SIZE * guid_index_in_rec] = | ||
94 | cpu_to_be64(sysadmin_ag_val); | ||
95 | |||
96 | /* Change the state to be pending for update */ | ||
97 | mdev->sriov.alias_guid.ports_guid[port->num - 1].all_rec_per_port[record_num].status | ||
98 | = MLX4_GUID_INFO_STATUS_IDLE ; | ||
99 | |||
100 | mdev->sriov.alias_guid.ports_guid[port->num - 1].all_rec_per_port[record_num].method | ||
101 | = MLX4_GUID_INFO_RECORD_SET; | ||
102 | |||
103 | switch (sysadmin_ag_val) { | ||
104 | case MLX4_GUID_FOR_DELETE_VAL: | ||
105 | mdev->sriov.alias_guid.ports_guid[port->num - 1].all_rec_per_port[record_num].method | ||
106 | = MLX4_GUID_INFO_RECORD_DELETE; | ||
107 | mdev->sriov.alias_guid.ports_guid[port->num - 1].all_rec_per_port[record_num].ownership | ||
108 | = MLX4_GUID_SYSADMIN_ASSIGN; | ||
109 | break; | ||
110 | /* The sysadmin requests the SM to re-assign */ | ||
111 | case MLX4_NOT_SET_GUID: | ||
112 | mdev->sriov.alias_guid.ports_guid[port->num - 1].all_rec_per_port[record_num].ownership | ||
113 | = MLX4_GUID_DRIVER_ASSIGN; | ||
114 | break; | ||
115 | /* The sysadmin requests a specific value.*/ | ||
116 | default: | ||
117 | mdev->sriov.alias_guid.ports_guid[port->num - 1].all_rec_per_port[record_num].ownership | ||
118 | = MLX4_GUID_SYSADMIN_ASSIGN; | ||
119 | break; | ||
120 | } | ||
121 | |||
122 | /* set the record index */ | ||
123 | mdev->sriov.alias_guid.ports_guid[port->num - 1].all_rec_per_port[record_num].guid_indexes | ||
124 | = mlx4_ib_get_aguid_comp_mask_from_ix(guid_index_in_rec); | ||
125 | |||
126 | mlx4_ib_init_alias_guid_work(mdev, port->num - 1); | ||
127 | |||
128 | return count; | ||
129 | } | ||
130 | |||
131 | static ssize_t show_port_gid(struct device *dev, | ||
132 | struct device_attribute *attr, | ||
133 | char *buf) | ||
134 | { | ||
135 | struct mlx4_ib_iov_sysfs_attr *mlx4_ib_iov_dentry = | ||
136 | container_of(attr, struct mlx4_ib_iov_sysfs_attr, dentry); | ||
137 | struct mlx4_ib_iov_port *port = mlx4_ib_iov_dentry->ctx; | ||
138 | struct mlx4_ib_dev *mdev = port->dev; | ||
139 | union ib_gid gid; | ||
140 | ssize_t ret; | ||
141 | |||
142 | ret = __mlx4_ib_query_gid(&mdev->ib_dev, port->num, | ||
143 | mlx4_ib_iov_dentry->entry_num, &gid, 1); | ||
144 | if (ret) | ||
145 | return ret; | ||
146 | ret = sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", | ||
147 | be16_to_cpu(((__be16 *) gid.raw)[0]), | ||
148 | be16_to_cpu(((__be16 *) gid.raw)[1]), | ||
149 | be16_to_cpu(((__be16 *) gid.raw)[2]), | ||
150 | be16_to_cpu(((__be16 *) gid.raw)[3]), | ||
151 | be16_to_cpu(((__be16 *) gid.raw)[4]), | ||
152 | be16_to_cpu(((__be16 *) gid.raw)[5]), | ||
153 | be16_to_cpu(((__be16 *) gid.raw)[6]), | ||
154 | be16_to_cpu(((__be16 *) gid.raw)[7])); | ||
155 | return ret; | ||
156 | } | ||
157 | |||
158 | static ssize_t show_phys_port_pkey(struct device *dev, | ||
159 | struct device_attribute *attr, | ||
160 | char *buf) | ||
161 | { | ||
162 | struct mlx4_ib_iov_sysfs_attr *mlx4_ib_iov_dentry = | ||
163 | container_of(attr, struct mlx4_ib_iov_sysfs_attr, dentry); | ||
164 | struct mlx4_ib_iov_port *port = mlx4_ib_iov_dentry->ctx; | ||
165 | struct mlx4_ib_dev *mdev = port->dev; | ||
166 | u16 pkey; | ||
167 | ssize_t ret; | ||
168 | |||
169 | ret = __mlx4_ib_query_pkey(&mdev->ib_dev, port->num, | ||
170 | mlx4_ib_iov_dentry->entry_num, &pkey, 1); | ||
171 | if (ret) | ||
172 | return ret; | ||
173 | |||
174 | return sprintf(buf, "0x%04x\n", pkey); | ||
175 | } | ||
176 | |||
177 | #define DENTRY_REMOVE(_dentry) \ | ||
178 | do { \ | ||
179 | sysfs_remove_file((_dentry)->kobj, &(_dentry)->dentry.attr); \ | ||
180 | } while (0); | ||
181 | |||
182 | static int create_sysfs_entry(void *_ctx, struct mlx4_ib_iov_sysfs_attr *_dentry, | ||
183 | char *_name, struct kobject *_kobj, | ||
184 | ssize_t (*show)(struct device *dev, | ||
185 | struct device_attribute *attr, | ||
186 | char *buf), | ||
187 | ssize_t (*store)(struct device *dev, | ||
188 | struct device_attribute *attr, | ||
189 | const char *buf, size_t count) | ||
190 | ) | ||
191 | { | ||
192 | int ret = 0; | ||
193 | struct mlx4_ib_iov_sysfs_attr *vdentry = _dentry; | ||
194 | |||
195 | vdentry->ctx = _ctx; | ||
196 | vdentry->dentry.show = show; | ||
197 | vdentry->dentry.store = store; | ||
198 | sysfs_attr_init(&vdentry->dentry.attr); | ||
199 | vdentry->dentry.attr.name = vdentry->name; | ||
200 | vdentry->dentry.attr.mode = 0; | ||
201 | vdentry->kobj = _kobj; | ||
202 | snprintf(vdentry->name, 15, "%s", _name); | ||
203 | |||
204 | if (vdentry->dentry.store) | ||
205 | vdentry->dentry.attr.mode |= S_IWUSR; | ||
206 | |||
207 | if (vdentry->dentry.show) | ||
208 | vdentry->dentry.attr.mode |= S_IRUGO; | ||
209 | |||
210 | ret = sysfs_create_file(vdentry->kobj, &vdentry->dentry.attr); | ||
211 | if (ret) { | ||
212 | pr_err("failed to create %s\n", vdentry->dentry.attr.name); | ||
213 | vdentry->ctx = NULL; | ||
214 | return ret; | ||
215 | } | ||
216 | |||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | int add_sysfs_port_mcg_attr(struct mlx4_ib_dev *device, int port_num, | ||
221 | struct attribute *attr) | ||
222 | { | ||
223 | struct mlx4_ib_iov_port *port = &device->iov_ports[port_num - 1]; | ||
224 | int ret; | ||
225 | |||
226 | ret = sysfs_create_file(port->mcgs_parent, attr); | ||
227 | if (ret) | ||
228 | pr_err("failed to create %s\n", attr->name); | ||
229 | |||
230 | return ret; | ||
231 | } | ||
232 | |||
233 | void del_sysfs_port_mcg_attr(struct mlx4_ib_dev *device, int port_num, | ||
234 | struct attribute *attr) | ||
235 | { | ||
236 | struct mlx4_ib_iov_port *port = &device->iov_ports[port_num - 1]; | ||
237 | |||
238 | sysfs_remove_file(port->mcgs_parent, attr); | ||
239 | } | ||
240 | |||
241 | static int add_port_entries(struct mlx4_ib_dev *device, int port_num) | ||
242 | { | ||
243 | int i; | ||
244 | char buff[10]; | ||
245 | struct mlx4_ib_iov_port *port = NULL; | ||
246 | int ret = 0 ; | ||
247 | struct ib_port_attr attr; | ||
248 | |||
249 | /* get the physical gid and pkey table sizes.*/ | ||
250 | ret = __mlx4_ib_query_port(&device->ib_dev, port_num, &attr, 1); | ||
251 | if (ret) | ||
252 | goto err; | ||
253 | |||
254 | port = &device->iov_ports[port_num - 1]; | ||
255 | port->dev = device; | ||
256 | port->num = port_num; | ||
257 | /* Directory structure: | ||
258 | * iov - | ||
259 | * port num - | ||
260 | * admin_guids | ||
261 | * gids (operational) | ||
262 | * mcg_table | ||
263 | */ | ||
264 | port->dentr_ar = kzalloc(sizeof (struct mlx4_ib_iov_sysfs_attr_ar), | ||
265 | GFP_KERNEL); | ||
266 | if (!port->dentr_ar) { | ||
267 | ret = -ENOMEM; | ||
268 | goto err; | ||
269 | } | ||
270 | sprintf(buff, "%d", port_num); | ||
271 | port->cur_port = kobject_create_and_add(buff, | ||
272 | kobject_get(device->ports_parent)); | ||
273 | if (!port->cur_port) { | ||
274 | ret = -ENOMEM; | ||
275 | goto kobj_create_err; | ||
276 | } | ||
277 | /* admin GUIDs */ | ||
278 | port->admin_alias_parent = kobject_create_and_add("admin_guids", | ||
279 | kobject_get(port->cur_port)); | ||
280 | if (!port->admin_alias_parent) { | ||
281 | ret = -ENOMEM; | ||
282 | goto err_admin_guids; | ||
283 | } | ||
284 | for (i = 0 ; i < attr.gid_tbl_len; i++) { | ||
285 | sprintf(buff, "%d", i); | ||
286 | port->dentr_ar->dentries[i].entry_num = i; | ||
287 | ret = create_sysfs_entry(port, &port->dentr_ar->dentries[i], | ||
288 | buff, port->admin_alias_parent, | ||
289 | show_admin_alias_guid, store_admin_alias_guid); | ||
290 | if (ret) | ||
291 | goto err_admin_alias_parent; | ||
292 | } | ||
293 | |||
294 | /* gids subdirectory (operational gids) */ | ||
295 | port->gids_parent = kobject_create_and_add("gids", | ||
296 | kobject_get(port->cur_port)); | ||
297 | if (!port->gids_parent) { | ||
298 | ret = -ENOMEM; | ||
299 | goto err_gids; | ||
300 | } | ||
301 | |||
302 | for (i = 0 ; i < attr.gid_tbl_len; i++) { | ||
303 | sprintf(buff, "%d", i); | ||
304 | port->dentr_ar->dentries[attr.gid_tbl_len + i].entry_num = i; | ||
305 | ret = create_sysfs_entry(port, | ||
306 | &port->dentr_ar->dentries[attr.gid_tbl_len + i], | ||
307 | buff, | ||
308 | port->gids_parent, show_port_gid, NULL); | ||
309 | if (ret) | ||
310 | goto err_gids_parent; | ||
311 | } | ||
312 | |||
313 | /* physical port pkey table */ | ||
314 | port->pkeys_parent = | ||
315 | kobject_create_and_add("pkeys", kobject_get(port->cur_port)); | ||
316 | if (!port->pkeys_parent) { | ||
317 | ret = -ENOMEM; | ||
318 | goto err_pkeys; | ||
319 | } | ||
320 | |||
321 | for (i = 0 ; i < attr.pkey_tbl_len; i++) { | ||
322 | sprintf(buff, "%d", i); | ||
323 | port->dentr_ar->dentries[2 * attr.gid_tbl_len + i].entry_num = i; | ||
324 | ret = create_sysfs_entry(port, | ||
325 | &port->dentr_ar->dentries[2 * attr.gid_tbl_len + i], | ||
326 | buff, port->pkeys_parent, | ||
327 | show_phys_port_pkey, NULL); | ||
328 | if (ret) | ||
329 | goto err_pkeys_parent; | ||
330 | } | ||
331 | |||
332 | /* MCGs table */ | ||
333 | port->mcgs_parent = | ||
334 | kobject_create_and_add("mcgs", kobject_get(port->cur_port)); | ||
335 | if (!port->mcgs_parent) { | ||
336 | ret = -ENOMEM; | ||
337 | goto err_mcgs; | ||
338 | } | ||
339 | return 0; | ||
340 | |||
341 | err_mcgs: | ||
342 | kobject_put(port->cur_port); | ||
343 | |||
344 | err_pkeys_parent: | ||
345 | kobject_put(port->pkeys_parent); | ||
346 | |||
347 | err_pkeys: | ||
348 | kobject_put(port->cur_port); | ||
349 | |||
350 | err_gids_parent: | ||
351 | kobject_put(port->gids_parent); | ||
352 | |||
353 | err_gids: | ||
354 | kobject_put(port->cur_port); | ||
355 | |||
356 | err_admin_alias_parent: | ||
357 | kobject_put(port->admin_alias_parent); | ||
358 | |||
359 | err_admin_guids: | ||
360 | kobject_put(port->cur_port); | ||
361 | kobject_put(port->cur_port); /* once more for create_and_add buff */ | ||
362 | |||
363 | kobj_create_err: | ||
364 | kobject_put(device->ports_parent); | ||
365 | kfree(port->dentr_ar); | ||
366 | |||
367 | err: | ||
368 | pr_err("add_port_entries FAILED: for port:%d, error: %d\n", | ||
369 | port_num, ret); | ||
370 | return ret; | ||
371 | } | ||
372 | |||
373 | static void get_name(struct mlx4_ib_dev *dev, char *name, int i, int max) | ||
374 | { | ||
375 | char base_name[9]; | ||
376 | |||
377 | /* pci_name format is: bus:dev:func -> xxxx:yy:zz.n */ | ||
378 | strlcpy(name, pci_name(dev->dev->pdev), max); | ||
379 | strncpy(base_name, name, 8); /*till xxxx:yy:*/ | ||
380 | base_name[8] = '\0'; | ||
381 | /* with no ARI only 3 last bits are used so when the fn is higher than 8 | ||
382 | * need to add it to the dev num, so count in the last number will be | ||
383 | * modulo 8 */ | ||
384 | sprintf(name, "%s%.2d.%d", base_name, (i/8), (i%8)); | ||
385 | } | ||
386 | |||
387 | struct mlx4_port { | ||
388 | struct kobject kobj; | ||
389 | struct mlx4_ib_dev *dev; | ||
390 | struct attribute_group pkey_group; | ||
391 | struct attribute_group gid_group; | ||
392 | u8 port_num; | ||
393 | int slave; | ||
394 | }; | ||
395 | |||
396 | |||
397 | static void mlx4_port_release(struct kobject *kobj) | ||
398 | { | ||
399 | struct mlx4_port *p = container_of(kobj, struct mlx4_port, kobj); | ||
400 | struct attribute *a; | ||
401 | int i; | ||
402 | |||
403 | for (i = 0; (a = p->pkey_group.attrs[i]); ++i) | ||
404 | kfree(a); | ||
405 | kfree(p->pkey_group.attrs); | ||
406 | for (i = 0; (a = p->gid_group.attrs[i]); ++i) | ||
407 | kfree(a); | ||
408 | kfree(p->gid_group.attrs); | ||
409 | kfree(p); | ||
410 | } | ||
411 | |||
412 | struct port_attribute { | ||
413 | struct attribute attr; | ||
414 | ssize_t (*show)(struct mlx4_port *, struct port_attribute *, char *buf); | ||
415 | ssize_t (*store)(struct mlx4_port *, struct port_attribute *, | ||
416 | const char *buf, size_t count); | ||
417 | }; | ||
418 | |||
419 | static ssize_t port_attr_show(struct kobject *kobj, | ||
420 | struct attribute *attr, char *buf) | ||
421 | { | ||
422 | struct port_attribute *port_attr = | ||
423 | container_of(attr, struct port_attribute, attr); | ||
424 | struct mlx4_port *p = container_of(kobj, struct mlx4_port, kobj); | ||
425 | |||
426 | if (!port_attr->show) | ||
427 | return -EIO; | ||
428 | return port_attr->show(p, port_attr, buf); | ||
429 | } | ||
430 | |||
431 | static ssize_t port_attr_store(struct kobject *kobj, | ||
432 | struct attribute *attr, | ||
433 | const char *buf, size_t size) | ||
434 | { | ||
435 | struct port_attribute *port_attr = | ||
436 | container_of(attr, struct port_attribute, attr); | ||
437 | struct mlx4_port *p = container_of(kobj, struct mlx4_port, kobj); | ||
438 | |||
439 | if (!port_attr->store) | ||
440 | return -EIO; | ||
441 | return port_attr->store(p, port_attr, buf, size); | ||
442 | } | ||
443 | |||
444 | static const struct sysfs_ops port_sysfs_ops = { | ||
445 | .show = port_attr_show, | ||
446 | .store = port_attr_store, | ||
447 | }; | ||
448 | |||
449 | static struct kobj_type port_type = { | ||
450 | .release = mlx4_port_release, | ||
451 | .sysfs_ops = &port_sysfs_ops, | ||
452 | }; | ||
453 | |||
454 | struct port_table_attribute { | ||
455 | struct port_attribute attr; | ||
456 | char name[8]; | ||
457 | int index; | ||
458 | }; | ||
459 | |||
460 | static ssize_t show_port_pkey(struct mlx4_port *p, struct port_attribute *attr, | ||
461 | char *buf) | ||
462 | { | ||
463 | struct port_table_attribute *tab_attr = | ||
464 | container_of(attr, struct port_table_attribute, attr); | ||
465 | ssize_t ret = -ENODEV; | ||
466 | |||
467 | if (p->dev->pkeys.virt2phys_pkey[p->slave][p->port_num - 1][tab_attr->index] >= | ||
468 | (p->dev->dev->caps.pkey_table_len[p->port_num])) | ||
469 | ret = sprintf(buf, "none\n"); | ||
470 | else | ||
471 | ret = sprintf(buf, "%d\n", | ||
472 | p->dev->pkeys.virt2phys_pkey[p->slave] | ||
473 | [p->port_num - 1][tab_attr->index]); | ||
474 | return ret; | ||
475 | } | ||
476 | |||
477 | static ssize_t store_port_pkey(struct mlx4_port *p, struct port_attribute *attr, | ||
478 | const char *buf, size_t count) | ||
479 | { | ||
480 | struct port_table_attribute *tab_attr = | ||
481 | container_of(attr, struct port_table_attribute, attr); | ||
482 | int idx; | ||
483 | int err; | ||
484 | |||
485 | /* do not allow remapping Dom0 virtual pkey table */ | ||
486 | if (p->slave == mlx4_master_func_num(p->dev->dev)) | ||
487 | return -EINVAL; | ||
488 | |||
489 | if (!strncasecmp(buf, "no", 2)) | ||
490 | idx = p->dev->dev->phys_caps.pkey_phys_table_len[p->port_num] - 1; | ||
491 | else if (sscanf(buf, "%i", &idx) != 1 || | ||
492 | idx >= p->dev->dev->caps.pkey_table_len[p->port_num] || | ||
493 | idx < 0) | ||
494 | return -EINVAL; | ||
495 | |||
496 | p->dev->pkeys.virt2phys_pkey[p->slave][p->port_num - 1] | ||
497 | [tab_attr->index] = idx; | ||
498 | mlx4_sync_pkey_table(p->dev->dev, p->slave, p->port_num, | ||
499 | tab_attr->index, idx); | ||
500 | err = mlx4_gen_pkey_eqe(p->dev->dev, p->slave, p->port_num); | ||
501 | if (err) { | ||
502 | pr_err("mlx4_gen_pkey_eqe failed for slave %d," | ||
503 | " port %d, index %d\n", p->slave, p->port_num, idx); | ||
504 | return err; | ||
505 | } | ||
506 | return count; | ||
507 | } | ||
508 | |||
509 | static ssize_t show_port_gid_idx(struct mlx4_port *p, | ||
510 | struct port_attribute *attr, char *buf) | ||
511 | { | ||
512 | return sprintf(buf, "%d\n", p->slave); | ||
513 | } | ||
514 | |||
515 | static struct attribute ** | ||
516 | alloc_group_attrs(ssize_t (*show)(struct mlx4_port *, | ||
517 | struct port_attribute *, char *buf), | ||
518 | ssize_t (*store)(struct mlx4_port *, struct port_attribute *, | ||
519 | const char *buf, size_t count), | ||
520 | int len) | ||
521 | { | ||
522 | struct attribute **tab_attr; | ||
523 | struct port_table_attribute *element; | ||
524 | int i; | ||
525 | |||
526 | tab_attr = kcalloc(1 + len, sizeof (struct attribute *), GFP_KERNEL); | ||
527 | if (!tab_attr) | ||
528 | return NULL; | ||
529 | |||
530 | for (i = 0; i < len; i++) { | ||
531 | element = kzalloc(sizeof (struct port_table_attribute), | ||
532 | GFP_KERNEL); | ||
533 | if (!element) | ||
534 | goto err; | ||
535 | if (snprintf(element->name, sizeof (element->name), | ||
536 | "%d", i) >= sizeof (element->name)) { | ||
537 | kfree(element); | ||
538 | goto err; | ||
539 | } | ||
540 | sysfs_attr_init(&element->attr.attr); | ||
541 | element->attr.attr.name = element->name; | ||
542 | if (store) { | ||
543 | element->attr.attr.mode = S_IWUSR | S_IRUGO; | ||
544 | element->attr.store = store; | ||
545 | } else | ||
546 | element->attr.attr.mode = S_IRUGO; | ||
547 | |||
548 | element->attr.show = show; | ||
549 | element->index = i; | ||
550 | tab_attr[i] = &element->attr.attr; | ||
551 | } | ||
552 | return tab_attr; | ||
553 | |||
554 | err: | ||
555 | while (--i >= 0) | ||
556 | kfree(tab_attr[i]); | ||
557 | kfree(tab_attr); | ||
558 | return NULL; | ||
559 | } | ||
560 | |||
561 | static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave) | ||
562 | { | ||
563 | struct mlx4_port *p; | ||
564 | int i; | ||
565 | int ret; | ||
566 | |||
567 | p = kzalloc(sizeof *p, GFP_KERNEL); | ||
568 | if (!p) | ||
569 | return -ENOMEM; | ||
570 | |||
571 | p->dev = dev; | ||
572 | p->port_num = port_num; | ||
573 | p->slave = slave; | ||
574 | |||
575 | ret = kobject_init_and_add(&p->kobj, &port_type, | ||
576 | kobject_get(dev->dev_ports_parent[slave]), | ||
577 | "%d", port_num); | ||
578 | if (ret) | ||
579 | goto err_alloc; | ||
580 | |||
581 | p->pkey_group.name = "pkey_idx"; | ||
582 | p->pkey_group.attrs = | ||
583 | alloc_group_attrs(show_port_pkey, store_port_pkey, | ||
584 | dev->dev->caps.pkey_table_len[port_num]); | ||
585 | if (!p->pkey_group.attrs) | ||
586 | goto err_alloc; | ||
587 | |||
588 | ret = sysfs_create_group(&p->kobj, &p->pkey_group); | ||
589 | if (ret) | ||
590 | goto err_free_pkey; | ||
591 | |||
592 | p->gid_group.name = "gid_idx"; | ||
593 | p->gid_group.attrs = alloc_group_attrs(show_port_gid_idx, NULL, 1); | ||
594 | if (!p->gid_group.attrs) | ||
595 | goto err_free_pkey; | ||
596 | |||
597 | ret = sysfs_create_group(&p->kobj, &p->gid_group); | ||
598 | if (ret) | ||
599 | goto err_free_gid; | ||
600 | |||
601 | list_add_tail(&p->kobj.entry, &dev->pkeys.pkey_port_list[slave]); | ||
602 | return 0; | ||
603 | |||
604 | err_free_gid: | ||
605 | kfree(p->gid_group.attrs[0]); | ||
606 | kfree(p->gid_group.attrs); | ||
607 | |||
608 | err_free_pkey: | ||
609 | for (i = 0; i < dev->dev->caps.pkey_table_len[port_num]; ++i) | ||
610 | kfree(p->pkey_group.attrs[i]); | ||
611 | kfree(p->pkey_group.attrs); | ||
612 | |||
613 | err_alloc: | ||
614 | kobject_put(dev->dev_ports_parent[slave]); | ||
615 | kfree(p); | ||
616 | return ret; | ||
617 | } | ||
618 | |||
619 | static int register_one_pkey_tree(struct mlx4_ib_dev *dev, int slave) | ||
620 | { | ||
621 | char name[32]; | ||
622 | int err; | ||
623 | int port; | ||
624 | struct kobject *p, *t; | ||
625 | struct mlx4_port *mport; | ||
626 | |||
627 | get_name(dev, name, slave, sizeof name); | ||
628 | |||
629 | dev->pkeys.device_parent[slave] = | ||
630 | kobject_create_and_add(name, kobject_get(dev->iov_parent)); | ||
631 | |||
632 | if (!dev->pkeys.device_parent[slave]) { | ||
633 | err = -ENOMEM; | ||
634 | goto fail_dev; | ||
635 | } | ||
636 | |||
637 | INIT_LIST_HEAD(&dev->pkeys.pkey_port_list[slave]); | ||
638 | |||
639 | dev->dev_ports_parent[slave] = | ||
640 | kobject_create_and_add("ports", | ||
641 | kobject_get(dev->pkeys.device_parent[slave])); | ||
642 | |||
643 | if (!dev->dev_ports_parent[slave]) { | ||
644 | err = -ENOMEM; | ||
645 | goto err_ports; | ||
646 | } | ||
647 | |||
648 | for (port = 1; port <= dev->dev->caps.num_ports; ++port) { | ||
649 | err = add_port(dev, port, slave); | ||
650 | if (err) | ||
651 | goto err_add; | ||
652 | } | ||
653 | return 0; | ||
654 | |||
655 | err_add: | ||
656 | list_for_each_entry_safe(p, t, | ||
657 | &dev->pkeys.pkey_port_list[slave], | ||
658 | entry) { | ||
659 | list_del(&p->entry); | ||
660 | mport = container_of(p, struct mlx4_port, kobj); | ||
661 | sysfs_remove_group(p, &mport->pkey_group); | ||
662 | sysfs_remove_group(p, &mport->gid_group); | ||
663 | kobject_put(p); | ||
664 | } | ||
665 | kobject_put(dev->dev_ports_parent[slave]); | ||
666 | |||
667 | err_ports: | ||
668 | kobject_put(dev->pkeys.device_parent[slave]); | ||
669 | /* extra put for the device_parent create_and_add */ | ||
670 | kobject_put(dev->pkeys.device_parent[slave]); | ||
671 | |||
672 | fail_dev: | ||
673 | kobject_put(dev->iov_parent); | ||
674 | return err; | ||
675 | } | ||
676 | |||
677 | static int register_pkey_tree(struct mlx4_ib_dev *device) | ||
678 | { | ||
679 | int i; | ||
680 | |||
681 | if (!mlx4_is_master(device->dev)) | ||
682 | return 0; | ||
683 | |||
684 | for (i = 0; i <= device->dev->num_vfs; ++i) | ||
685 | register_one_pkey_tree(device, i); | ||
686 | |||
687 | return 0; | ||
688 | } | ||
689 | |||
690 | static void unregister_pkey_tree(struct mlx4_ib_dev *device) | ||
691 | { | ||
692 | int slave; | ||
693 | struct kobject *p, *t; | ||
694 | struct mlx4_port *port; | ||
695 | |||
696 | if (!mlx4_is_master(device->dev)) | ||
697 | return; | ||
698 | |||
699 | for (slave = device->dev->num_vfs; slave >= 0; --slave) { | ||
700 | list_for_each_entry_safe(p, t, | ||
701 | &device->pkeys.pkey_port_list[slave], | ||
702 | entry) { | ||
703 | list_del(&p->entry); | ||
704 | port = container_of(p, struct mlx4_port, kobj); | ||
705 | sysfs_remove_group(p, &port->pkey_group); | ||
706 | sysfs_remove_group(p, &port->gid_group); | ||
707 | kobject_put(p); | ||
708 | kobject_put(device->dev_ports_parent[slave]); | ||
709 | } | ||
710 | kobject_put(device->dev_ports_parent[slave]); | ||
711 | kobject_put(device->pkeys.device_parent[slave]); | ||
712 | kobject_put(device->pkeys.device_parent[slave]); | ||
713 | kobject_put(device->iov_parent); | ||
714 | } | ||
715 | } | ||
716 | |||
717 | int mlx4_ib_device_register_sysfs(struct mlx4_ib_dev *dev) | ||
718 | { | ||
719 | int i; | ||
720 | int ret = 0; | ||
721 | |||
722 | if (!mlx4_is_master(dev->dev)) | ||
723 | return 0; | ||
724 | |||
725 | dev->iov_parent = | ||
726 | kobject_create_and_add("iov", | ||
727 | kobject_get(dev->ib_dev.ports_parent->parent)); | ||
728 | if (!dev->iov_parent) { | ||
729 | ret = -ENOMEM; | ||
730 | goto err; | ||
731 | } | ||
732 | dev->ports_parent = | ||
733 | kobject_create_and_add("ports", | ||
734 | kobject_get(dev->iov_parent)); | ||
735 | if (!dev->iov_parent) { | ||
736 | ret = -ENOMEM; | ||
737 | goto err_ports; | ||
738 | } | ||
739 | |||
740 | for (i = 1; i <= dev->ib_dev.phys_port_cnt; ++i) { | ||
741 | ret = add_port_entries(dev, i); | ||
742 | if (ret) | ||
743 | goto err_add_entries; | ||
744 | } | ||
745 | |||
746 | ret = register_pkey_tree(dev); | ||
747 | if (ret) | ||
748 | goto err_add_entries; | ||
749 | return 0; | ||
750 | |||
751 | err_add_entries: | ||
752 | kobject_put(dev->ports_parent); | ||
753 | |||
754 | err_ports: | ||
755 | kobject_put(dev->iov_parent); | ||
756 | err: | ||
757 | kobject_put(dev->ib_dev.ports_parent->parent); | ||
758 | pr_err("mlx4_ib_device_register_sysfs error (%d)\n", ret); | ||
759 | return ret; | ||
760 | } | ||
761 | |||
762 | static void unregister_alias_guid_tree(struct mlx4_ib_dev *device) | ||
763 | { | ||
764 | struct mlx4_ib_iov_port *p; | ||
765 | int i; | ||
766 | |||
767 | if (!mlx4_is_master(device->dev)) | ||
768 | return; | ||
769 | |||
770 | for (i = 0; i < device->dev->caps.num_ports; i++) { | ||
771 | p = &device->iov_ports[i]; | ||
772 | kobject_put(p->admin_alias_parent); | ||
773 | kobject_put(p->gids_parent); | ||
774 | kobject_put(p->pkeys_parent); | ||
775 | kobject_put(p->mcgs_parent); | ||
776 | kobject_put(p->cur_port); | ||
777 | kobject_put(p->cur_port); | ||
778 | kobject_put(p->cur_port); | ||
779 | kobject_put(p->cur_port); | ||
780 | kobject_put(p->cur_port); | ||
781 | kobject_put(p->dev->ports_parent); | ||
782 | kfree(p->dentr_ar); | ||
783 | } | ||
784 | } | ||
785 | |||
786 | void mlx4_ib_device_unregister_sysfs(struct mlx4_ib_dev *device) | ||
787 | { | ||
788 | unregister_alias_guid_tree(device); | ||
789 | unregister_pkey_tree(device); | ||
790 | kobject_put(device->ports_parent); | ||
791 | kobject_put(device->iov_parent); | ||
792 | kobject_put(device->iov_parent); | ||
793 | kobject_put(device->ib_dev.ports_parent->parent); | ||
794 | } | ||