diff options
Diffstat (limited to 'drivers/scsi/fcoe/fcoe_sysfs.c')
-rw-r--r-- | drivers/scsi/fcoe/fcoe_sysfs.c | 832 |
1 files changed, 832 insertions, 0 deletions
diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c new file mode 100644 index 000000000000..2bc163198d33 --- /dev/null +++ b/drivers/scsi/fcoe/fcoe_sysfs.c | |||
@@ -0,0 +1,832 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2011 - 2012 Intel Corporation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program; if not, write to the Free Software Foundation, Inc., | ||
15 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
16 | * | ||
17 | * Maintained at www.Open-FCoE.org | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/types.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/etherdevice.h> | ||
24 | |||
25 | #include <scsi/fcoe_sysfs.h> | ||
26 | |||
27 | static atomic_t ctlr_num; | ||
28 | static atomic_t fcf_num; | ||
29 | |||
30 | /* | ||
31 | * fcoe_fcf_dev_loss_tmo: the default number of seconds that fcoe sysfs | ||
32 | * should insulate the loss of a fcf. | ||
33 | */ | ||
34 | static unsigned int fcoe_fcf_dev_loss_tmo = 1800; /* seconds */ | ||
35 | |||
36 | module_param_named(fcf_dev_loss_tmo, fcoe_fcf_dev_loss_tmo, | ||
37 | uint, S_IRUGO|S_IWUSR); | ||
38 | MODULE_PARM_DESC(fcf_dev_loss_tmo, | ||
39 | "Maximum number of seconds that libfcoe should" | ||
40 | " insulate the loss of a fcf. Once this value is" | ||
41 | " exceeded, the fcf is removed."); | ||
42 | |||
43 | /* | ||
44 | * These are used by the fcoe_*_show_function routines, they | ||
45 | * are intentionally placed in the .c file as they're not intended | ||
46 | * for use throughout the code. | ||
47 | */ | ||
48 | #define fcoe_ctlr_id(x) \ | ||
49 | ((x)->id) | ||
50 | #define fcoe_ctlr_work_q_name(x) \ | ||
51 | ((x)->work_q_name) | ||
52 | #define fcoe_ctlr_work_q(x) \ | ||
53 | ((x)->work_q) | ||
54 | #define fcoe_ctlr_devloss_work_q_name(x) \ | ||
55 | ((x)->devloss_work_q_name) | ||
56 | #define fcoe_ctlr_devloss_work_q(x) \ | ||
57 | ((x)->devloss_work_q) | ||
58 | #define fcoe_ctlr_mode(x) \ | ||
59 | ((x)->mode) | ||
60 | #define fcoe_ctlr_fcf_dev_loss_tmo(x) \ | ||
61 | ((x)->fcf_dev_loss_tmo) | ||
62 | #define fcoe_ctlr_link_fail(x) \ | ||
63 | ((x)->lesb.lesb_link_fail) | ||
64 | #define fcoe_ctlr_vlink_fail(x) \ | ||
65 | ((x)->lesb.lesb_vlink_fail) | ||
66 | #define fcoe_ctlr_miss_fka(x) \ | ||
67 | ((x)->lesb.lesb_miss_fka) | ||
68 | #define fcoe_ctlr_symb_err(x) \ | ||
69 | ((x)->lesb.lesb_symb_err) | ||
70 | #define fcoe_ctlr_err_block(x) \ | ||
71 | ((x)->lesb.lesb_err_block) | ||
72 | #define fcoe_ctlr_fcs_error(x) \ | ||
73 | ((x)->lesb.lesb_fcs_error) | ||
74 | #define fcoe_fcf_state(x) \ | ||
75 | ((x)->state) | ||
76 | #define fcoe_fcf_fabric_name(x) \ | ||
77 | ((x)->fabric_name) | ||
78 | #define fcoe_fcf_switch_name(x) \ | ||
79 | ((x)->switch_name) | ||
80 | #define fcoe_fcf_fc_map(x) \ | ||
81 | ((x)->fc_map) | ||
82 | #define fcoe_fcf_vfid(x) \ | ||
83 | ((x)->vfid) | ||
84 | #define fcoe_fcf_mac(x) \ | ||
85 | ((x)->mac) | ||
86 | #define fcoe_fcf_priority(x) \ | ||
87 | ((x)->priority) | ||
88 | #define fcoe_fcf_fka_period(x) \ | ||
89 | ((x)->fka_period) | ||
90 | #define fcoe_fcf_dev_loss_tmo(x) \ | ||
91 | ((x)->dev_loss_tmo) | ||
92 | #define fcoe_fcf_selected(x) \ | ||
93 | ((x)->selected) | ||
94 | #define fcoe_fcf_vlan_id(x) \ | ||
95 | ((x)->vlan_id) | ||
96 | |||
97 | /* | ||
98 | * dev_loss_tmo attribute | ||
99 | */ | ||
100 | static int fcoe_str_to_dev_loss(const char *buf, unsigned long *val) | ||
101 | { | ||
102 | int ret; | ||
103 | |||
104 | ret = kstrtoul(buf, 0, val); | ||
105 | if (ret || *val < 0) | ||
106 | return -EINVAL; | ||
107 | /* | ||
108 | * Check for overflow; dev_loss_tmo is u32 | ||
109 | */ | ||
110 | if (*val > UINT_MAX) | ||
111 | return -EINVAL; | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | static int fcoe_fcf_set_dev_loss_tmo(struct fcoe_fcf_device *fcf, | ||
117 | unsigned long val) | ||
118 | { | ||
119 | if ((fcf->state == FCOE_FCF_STATE_UNKNOWN) || | ||
120 | (fcf->state == FCOE_FCF_STATE_DISCONNECTED) || | ||
121 | (fcf->state == FCOE_FCF_STATE_DELETED)) | ||
122 | return -EBUSY; | ||
123 | /* | ||
124 | * Check for overflow; dev_loss_tmo is u32 | ||
125 | */ | ||
126 | if (val > UINT_MAX) | ||
127 | return -EINVAL; | ||
128 | |||
129 | fcoe_fcf_dev_loss_tmo(fcf) = val; | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | #define FCOE_DEVICE_ATTR(_prefix, _name, _mode, _show, _store) \ | ||
134 | struct device_attribute device_attr_fcoe_##_prefix##_##_name = \ | ||
135 | __ATTR(_name, _mode, _show, _store) | ||
136 | |||
137 | #define fcoe_ctlr_show_function(field, format_string, sz, cast) \ | ||
138 | static ssize_t show_fcoe_ctlr_device_##field(struct device *dev, \ | ||
139 | struct device_attribute *attr, \ | ||
140 | char *buf) \ | ||
141 | { \ | ||
142 | struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); \ | ||
143 | if (ctlr->f->get_fcoe_ctlr_##field) \ | ||
144 | ctlr->f->get_fcoe_ctlr_##field(ctlr); \ | ||
145 | return snprintf(buf, sz, format_string, \ | ||
146 | cast fcoe_ctlr_##field(ctlr)); \ | ||
147 | } | ||
148 | |||
149 | #define fcoe_fcf_show_function(field, format_string, sz, cast) \ | ||
150 | static ssize_t show_fcoe_fcf_device_##field(struct device *dev, \ | ||
151 | struct device_attribute *attr, \ | ||
152 | char *buf) \ | ||
153 | { \ | ||
154 | struct fcoe_fcf_device *fcf = dev_to_fcf(dev); \ | ||
155 | struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf); \ | ||
156 | if (ctlr->f->get_fcoe_fcf_##field) \ | ||
157 | ctlr->f->get_fcoe_fcf_##field(fcf); \ | ||
158 | return snprintf(buf, sz, format_string, \ | ||
159 | cast fcoe_fcf_##field(fcf)); \ | ||
160 | } | ||
161 | |||
162 | #define fcoe_ctlr_private_show_function(field, format_string, sz, cast) \ | ||
163 | static ssize_t show_fcoe_ctlr_device_##field(struct device *dev, \ | ||
164 | struct device_attribute *attr, \ | ||
165 | char *buf) \ | ||
166 | { \ | ||
167 | struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); \ | ||
168 | return snprintf(buf, sz, format_string, cast fcoe_ctlr_##field(ctlr)); \ | ||
169 | } | ||
170 | |||
171 | #define fcoe_fcf_private_show_function(field, format_string, sz, cast) \ | ||
172 | static ssize_t show_fcoe_fcf_device_##field(struct device *dev, \ | ||
173 | struct device_attribute *attr, \ | ||
174 | char *buf) \ | ||
175 | { \ | ||
176 | struct fcoe_fcf_device *fcf = dev_to_fcf(dev); \ | ||
177 | return snprintf(buf, sz, format_string, cast fcoe_fcf_##field(fcf)); \ | ||
178 | } | ||
179 | |||
180 | #define fcoe_ctlr_private_rd_attr(field, format_string, sz) \ | ||
181 | fcoe_ctlr_private_show_function(field, format_string, sz, ) \ | ||
182 | static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO, \ | ||
183 | show_fcoe_ctlr_device_##field, NULL) | ||
184 | |||
185 | #define fcoe_ctlr_rd_attr(field, format_string, sz) \ | ||
186 | fcoe_ctlr_show_function(field, format_string, sz, ) \ | ||
187 | static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO, \ | ||
188 | show_fcoe_ctlr_device_##field, NULL) | ||
189 | |||
190 | #define fcoe_fcf_rd_attr(field, format_string, sz) \ | ||
191 | fcoe_fcf_show_function(field, format_string, sz, ) \ | ||
192 | static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO, \ | ||
193 | show_fcoe_fcf_device_##field, NULL) | ||
194 | |||
195 | #define fcoe_fcf_private_rd_attr(field, format_string, sz) \ | ||
196 | fcoe_fcf_private_show_function(field, format_string, sz, ) \ | ||
197 | static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO, \ | ||
198 | show_fcoe_fcf_device_##field, NULL) | ||
199 | |||
200 | #define fcoe_ctlr_private_rd_attr_cast(field, format_string, sz, cast) \ | ||
201 | fcoe_ctlr_private_show_function(field, format_string, sz, (cast)) \ | ||
202 | static FCOE_DEVICE_ATTR(ctlr, field, S_IRUGO, \ | ||
203 | show_fcoe_ctlr_device_##field, NULL) | ||
204 | |||
205 | #define fcoe_fcf_private_rd_attr_cast(field, format_string, sz, cast) \ | ||
206 | fcoe_fcf_private_show_function(field, format_string, sz, (cast)) \ | ||
207 | static FCOE_DEVICE_ATTR(fcf, field, S_IRUGO, \ | ||
208 | show_fcoe_fcf_device_##field, NULL) | ||
209 | |||
210 | #define fcoe_enum_name_search(title, table_type, table) \ | ||
211 | static const char *get_fcoe_##title##_name(enum table_type table_key) \ | ||
212 | { \ | ||
213 | int i; \ | ||
214 | char *name = NULL; \ | ||
215 | \ | ||
216 | for (i = 0; i < ARRAY_SIZE(table); i++) { \ | ||
217 | if (table[i].value == table_key) { \ | ||
218 | name = table[i].name; \ | ||
219 | break; \ | ||
220 | } \ | ||
221 | } \ | ||
222 | return name; \ | ||
223 | } | ||
224 | |||
225 | static struct { | ||
226 | enum fcf_state value; | ||
227 | char *name; | ||
228 | } fcf_state_names[] = { | ||
229 | { FCOE_FCF_STATE_UNKNOWN, "Unknown" }, | ||
230 | { FCOE_FCF_STATE_DISCONNECTED, "Disconnected" }, | ||
231 | { FCOE_FCF_STATE_CONNECTED, "Connected" }, | ||
232 | }; | ||
233 | fcoe_enum_name_search(fcf_state, fcf_state, fcf_state_names) | ||
234 | #define FCOE_FCF_STATE_MAX_NAMELEN 50 | ||
235 | |||
236 | static ssize_t show_fcf_state(struct device *dev, | ||
237 | struct device_attribute *attr, | ||
238 | char *buf) | ||
239 | { | ||
240 | struct fcoe_fcf_device *fcf = dev_to_fcf(dev); | ||
241 | const char *name; | ||
242 | name = get_fcoe_fcf_state_name(fcf->state); | ||
243 | if (!name) | ||
244 | return -EINVAL; | ||
245 | return snprintf(buf, FCOE_FCF_STATE_MAX_NAMELEN, "%s\n", name); | ||
246 | } | ||
247 | static FCOE_DEVICE_ATTR(fcf, state, S_IRUGO, show_fcf_state, NULL); | ||
248 | |||
249 | static struct { | ||
250 | enum fip_conn_type value; | ||
251 | char *name; | ||
252 | } fip_conn_type_names[] = { | ||
253 | { FIP_CONN_TYPE_UNKNOWN, "Unknown" }, | ||
254 | { FIP_CONN_TYPE_FABRIC, "Fabric" }, | ||
255 | { FIP_CONN_TYPE_VN2VN, "VN2VN" }, | ||
256 | }; | ||
257 | fcoe_enum_name_search(ctlr_mode, fip_conn_type, fip_conn_type_names) | ||
258 | #define FCOE_CTLR_MODE_MAX_NAMELEN 50 | ||
259 | |||
260 | static ssize_t show_ctlr_mode(struct device *dev, | ||
261 | struct device_attribute *attr, | ||
262 | char *buf) | ||
263 | { | ||
264 | struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); | ||
265 | const char *name; | ||
266 | |||
267 | if (ctlr->f->get_fcoe_ctlr_mode) | ||
268 | ctlr->f->get_fcoe_ctlr_mode(ctlr); | ||
269 | |||
270 | name = get_fcoe_ctlr_mode_name(ctlr->mode); | ||
271 | if (!name) | ||
272 | return -EINVAL; | ||
273 | return snprintf(buf, FCOE_CTLR_MODE_MAX_NAMELEN, | ||
274 | "%s\n", name); | ||
275 | } | ||
276 | static FCOE_DEVICE_ATTR(ctlr, mode, S_IRUGO, | ||
277 | show_ctlr_mode, NULL); | ||
278 | |||
279 | static ssize_t | ||
280 | store_private_fcoe_ctlr_fcf_dev_loss_tmo(struct device *dev, | ||
281 | struct device_attribute *attr, | ||
282 | const char *buf, size_t count) | ||
283 | { | ||
284 | struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); | ||
285 | struct fcoe_fcf_device *fcf; | ||
286 | unsigned long val; | ||
287 | int rc; | ||
288 | |||
289 | rc = fcoe_str_to_dev_loss(buf, &val); | ||
290 | if (rc) | ||
291 | return rc; | ||
292 | |||
293 | fcoe_ctlr_fcf_dev_loss_tmo(ctlr) = val; | ||
294 | mutex_lock(&ctlr->lock); | ||
295 | list_for_each_entry(fcf, &ctlr->fcfs, peers) | ||
296 | fcoe_fcf_set_dev_loss_tmo(fcf, val); | ||
297 | mutex_unlock(&ctlr->lock); | ||
298 | return count; | ||
299 | } | ||
300 | fcoe_ctlr_private_show_function(fcf_dev_loss_tmo, "%d\n", 20, ); | ||
301 | static FCOE_DEVICE_ATTR(ctlr, fcf_dev_loss_tmo, S_IRUGO | S_IWUSR, | ||
302 | show_fcoe_ctlr_device_fcf_dev_loss_tmo, | ||
303 | store_private_fcoe_ctlr_fcf_dev_loss_tmo); | ||
304 | |||
305 | /* Link Error Status Block (LESB) */ | ||
306 | fcoe_ctlr_rd_attr(link_fail, "%u\n", 20); | ||
307 | fcoe_ctlr_rd_attr(vlink_fail, "%u\n", 20); | ||
308 | fcoe_ctlr_rd_attr(miss_fka, "%u\n", 20); | ||
309 | fcoe_ctlr_rd_attr(symb_err, "%u\n", 20); | ||
310 | fcoe_ctlr_rd_attr(err_block, "%u\n", 20); | ||
311 | fcoe_ctlr_rd_attr(fcs_error, "%u\n", 20); | ||
312 | |||
313 | fcoe_fcf_private_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long); | ||
314 | fcoe_fcf_private_rd_attr_cast(switch_name, "0x%llx\n", 20, unsigned long long); | ||
315 | fcoe_fcf_private_rd_attr(priority, "%u\n", 20); | ||
316 | fcoe_fcf_private_rd_attr(fc_map, "0x%x\n", 20); | ||
317 | fcoe_fcf_private_rd_attr(vfid, "%u\n", 20); | ||
318 | fcoe_fcf_private_rd_attr(mac, "%pM\n", 20); | ||
319 | fcoe_fcf_private_rd_attr(fka_period, "%u\n", 20); | ||
320 | fcoe_fcf_rd_attr(selected, "%u\n", 20); | ||
321 | fcoe_fcf_rd_attr(vlan_id, "%u\n", 20); | ||
322 | |||
323 | fcoe_fcf_private_show_function(dev_loss_tmo, "%d\n", 20, ) | ||
324 | static ssize_t | ||
325 | store_fcoe_fcf_dev_loss_tmo(struct device *dev, struct device_attribute *attr, | ||
326 | const char *buf, size_t count) | ||
327 | { | ||
328 | struct fcoe_fcf_device *fcf = dev_to_fcf(dev); | ||
329 | unsigned long val; | ||
330 | int rc; | ||
331 | |||
332 | rc = fcoe_str_to_dev_loss(buf, &val); | ||
333 | if (rc) | ||
334 | return rc; | ||
335 | |||
336 | rc = fcoe_fcf_set_dev_loss_tmo(fcf, val); | ||
337 | if (rc) | ||
338 | return rc; | ||
339 | return count; | ||
340 | } | ||
341 | static FCOE_DEVICE_ATTR(fcf, dev_loss_tmo, S_IRUGO | S_IWUSR, | ||
342 | show_fcoe_fcf_device_dev_loss_tmo, | ||
343 | store_fcoe_fcf_dev_loss_tmo); | ||
344 | |||
345 | static struct attribute *fcoe_ctlr_lesb_attrs[] = { | ||
346 | &device_attr_fcoe_ctlr_link_fail.attr, | ||
347 | &device_attr_fcoe_ctlr_vlink_fail.attr, | ||
348 | &device_attr_fcoe_ctlr_miss_fka.attr, | ||
349 | &device_attr_fcoe_ctlr_symb_err.attr, | ||
350 | &device_attr_fcoe_ctlr_err_block.attr, | ||
351 | &device_attr_fcoe_ctlr_fcs_error.attr, | ||
352 | NULL, | ||
353 | }; | ||
354 | |||
355 | static struct attribute_group fcoe_ctlr_lesb_attr_group = { | ||
356 | .name = "lesb", | ||
357 | .attrs = fcoe_ctlr_lesb_attrs, | ||
358 | }; | ||
359 | |||
360 | static struct attribute *fcoe_ctlr_attrs[] = { | ||
361 | &device_attr_fcoe_ctlr_fcf_dev_loss_tmo.attr, | ||
362 | &device_attr_fcoe_ctlr_mode.attr, | ||
363 | NULL, | ||
364 | }; | ||
365 | |||
366 | static struct attribute_group fcoe_ctlr_attr_group = { | ||
367 | .attrs = fcoe_ctlr_attrs, | ||
368 | }; | ||
369 | |||
370 | static const struct attribute_group *fcoe_ctlr_attr_groups[] = { | ||
371 | &fcoe_ctlr_attr_group, | ||
372 | &fcoe_ctlr_lesb_attr_group, | ||
373 | NULL, | ||
374 | }; | ||
375 | |||
376 | static struct attribute *fcoe_fcf_attrs[] = { | ||
377 | &device_attr_fcoe_fcf_fabric_name.attr, | ||
378 | &device_attr_fcoe_fcf_switch_name.attr, | ||
379 | &device_attr_fcoe_fcf_dev_loss_tmo.attr, | ||
380 | &device_attr_fcoe_fcf_fc_map.attr, | ||
381 | &device_attr_fcoe_fcf_vfid.attr, | ||
382 | &device_attr_fcoe_fcf_mac.attr, | ||
383 | &device_attr_fcoe_fcf_priority.attr, | ||
384 | &device_attr_fcoe_fcf_fka_period.attr, | ||
385 | &device_attr_fcoe_fcf_state.attr, | ||
386 | &device_attr_fcoe_fcf_selected.attr, | ||
387 | &device_attr_fcoe_fcf_vlan_id.attr, | ||
388 | NULL | ||
389 | }; | ||
390 | |||
391 | static struct attribute_group fcoe_fcf_attr_group = { | ||
392 | .attrs = fcoe_fcf_attrs, | ||
393 | }; | ||
394 | |||
395 | static const struct attribute_group *fcoe_fcf_attr_groups[] = { | ||
396 | &fcoe_fcf_attr_group, | ||
397 | NULL, | ||
398 | }; | ||
399 | |||
400 | struct bus_type fcoe_bus_type; | ||
401 | |||
402 | static int fcoe_bus_match(struct device *dev, | ||
403 | struct device_driver *drv) | ||
404 | { | ||
405 | if (dev->bus == &fcoe_bus_type) | ||
406 | return 1; | ||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | /** | ||
411 | * fcoe_ctlr_device_release() - Release the FIP ctlr memory | ||
412 | * @dev: Pointer to the FIP ctlr's embedded device | ||
413 | * | ||
414 | * Called when the last FIP ctlr reference is released. | ||
415 | */ | ||
416 | static void fcoe_ctlr_device_release(struct device *dev) | ||
417 | { | ||
418 | struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); | ||
419 | kfree(ctlr); | ||
420 | } | ||
421 | |||
422 | /** | ||
423 | * fcoe_fcf_device_release() - Release the FIP fcf memory | ||
424 | * @dev: Pointer to the fcf's embedded device | ||
425 | * | ||
426 | * Called when the last FIP fcf reference is released. | ||
427 | */ | ||
428 | static void fcoe_fcf_device_release(struct device *dev) | ||
429 | { | ||
430 | struct fcoe_fcf_device *fcf = dev_to_fcf(dev); | ||
431 | kfree(fcf); | ||
432 | } | ||
433 | |||
434 | struct device_type fcoe_ctlr_device_type = { | ||
435 | .name = "fcoe_ctlr", | ||
436 | .groups = fcoe_ctlr_attr_groups, | ||
437 | .release = fcoe_ctlr_device_release, | ||
438 | }; | ||
439 | |||
440 | struct device_type fcoe_fcf_device_type = { | ||
441 | .name = "fcoe_fcf", | ||
442 | .groups = fcoe_fcf_attr_groups, | ||
443 | .release = fcoe_fcf_device_release, | ||
444 | }; | ||
445 | |||
446 | struct bus_type fcoe_bus_type = { | ||
447 | .name = "fcoe", | ||
448 | .match = &fcoe_bus_match, | ||
449 | }; | ||
450 | |||
451 | /** | ||
452 | * fcoe_ctlr_device_flush_work() - Flush a FIP ctlr's workqueue | ||
453 | * @ctlr: Pointer to the FIP ctlr whose workqueue is to be flushed | ||
454 | */ | ||
455 | void fcoe_ctlr_device_flush_work(struct fcoe_ctlr_device *ctlr) | ||
456 | { | ||
457 | if (!fcoe_ctlr_work_q(ctlr)) { | ||
458 | printk(KERN_ERR | ||
459 | "ERROR: FIP Ctlr '%d' attempted to flush work, " | ||
460 | "when no workqueue created.\n", ctlr->id); | ||
461 | dump_stack(); | ||
462 | return; | ||
463 | } | ||
464 | |||
465 | flush_workqueue(fcoe_ctlr_work_q(ctlr)); | ||
466 | } | ||
467 | |||
468 | /** | ||
469 | * fcoe_ctlr_device_queue_work() - Schedule work for a FIP ctlr's workqueue | ||
470 | * @ctlr: Pointer to the FIP ctlr who owns the devloss workqueue | ||
471 | * @work: Work to queue for execution | ||
472 | * | ||
473 | * Return value: | ||
474 | * 1 on success / 0 already queued / < 0 for error | ||
475 | */ | ||
476 | int fcoe_ctlr_device_queue_work(struct fcoe_ctlr_device *ctlr, | ||
477 | struct work_struct *work) | ||
478 | { | ||
479 | if (unlikely(!fcoe_ctlr_work_q(ctlr))) { | ||
480 | printk(KERN_ERR | ||
481 | "ERROR: FIP Ctlr '%d' attempted to queue work, " | ||
482 | "when no workqueue created.\n", ctlr->id); | ||
483 | dump_stack(); | ||
484 | |||
485 | return -EINVAL; | ||
486 | } | ||
487 | |||
488 | return queue_work(fcoe_ctlr_work_q(ctlr), work); | ||
489 | } | ||
490 | |||
491 | /** | ||
492 | * fcoe_ctlr_device_flush_devloss() - Flush a FIP ctlr's devloss workqueue | ||
493 | * @ctlr: Pointer to FIP ctlr whose workqueue is to be flushed | ||
494 | */ | ||
495 | void fcoe_ctlr_device_flush_devloss(struct fcoe_ctlr_device *ctlr) | ||
496 | { | ||
497 | if (!fcoe_ctlr_devloss_work_q(ctlr)) { | ||
498 | printk(KERN_ERR | ||
499 | "ERROR: FIP Ctlr '%d' attempted to flush work, " | ||
500 | "when no workqueue created.\n", ctlr->id); | ||
501 | dump_stack(); | ||
502 | return; | ||
503 | } | ||
504 | |||
505 | flush_workqueue(fcoe_ctlr_devloss_work_q(ctlr)); | ||
506 | } | ||
507 | |||
508 | /** | ||
509 | * fcoe_ctlr_device_queue_devloss_work() - Schedule work for a FIP ctlr's devloss workqueue | ||
510 | * @ctlr: Pointer to the FIP ctlr who owns the devloss workqueue | ||
511 | * @work: Work to queue for execution | ||
512 | * @delay: jiffies to delay the work queuing | ||
513 | * | ||
514 | * Return value: | ||
515 | * 1 on success / 0 already queued / < 0 for error | ||
516 | */ | ||
517 | int fcoe_ctlr_device_queue_devloss_work(struct fcoe_ctlr_device *ctlr, | ||
518 | struct delayed_work *work, | ||
519 | unsigned long delay) | ||
520 | { | ||
521 | if (unlikely(!fcoe_ctlr_devloss_work_q(ctlr))) { | ||
522 | printk(KERN_ERR | ||
523 | "ERROR: FIP Ctlr '%d' attempted to queue work, " | ||
524 | "when no workqueue created.\n", ctlr->id); | ||
525 | dump_stack(); | ||
526 | |||
527 | return -EINVAL; | ||
528 | } | ||
529 | |||
530 | return queue_delayed_work(fcoe_ctlr_devloss_work_q(ctlr), work, delay); | ||
531 | } | ||
532 | |||
533 | static int fcoe_fcf_device_match(struct fcoe_fcf_device *new, | ||
534 | struct fcoe_fcf_device *old) | ||
535 | { | ||
536 | if (new->switch_name == old->switch_name && | ||
537 | new->fabric_name == old->fabric_name && | ||
538 | new->fc_map == old->fc_map && | ||
539 | compare_ether_addr(new->mac, old->mac) == 0) | ||
540 | return 1; | ||
541 | return 0; | ||
542 | } | ||
543 | |||
544 | /** | ||
545 | * fcoe_ctlr_device_add() - Add a FIP ctlr to sysfs | ||
546 | * @parent: The parent device to which the fcoe_ctlr instance | ||
547 | * should be attached | ||
548 | * @f: The LLD's FCoE sysfs function template pointer | ||
549 | * @priv_size: Size to be allocated with the fcoe_ctlr_device for the LLD | ||
550 | * | ||
551 | * This routine allocates a FIP ctlr object with some additional memory | ||
552 | * for the LLD. The FIP ctlr is initialized, added to sysfs and then | ||
553 | * attributes are added to it. | ||
554 | */ | ||
555 | struct fcoe_ctlr_device *fcoe_ctlr_device_add(struct device *parent, | ||
556 | struct fcoe_sysfs_function_template *f, | ||
557 | int priv_size) | ||
558 | { | ||
559 | struct fcoe_ctlr_device *ctlr; | ||
560 | int error = 0; | ||
561 | |||
562 | ctlr = kzalloc(sizeof(struct fcoe_ctlr_device) + priv_size, | ||
563 | GFP_KERNEL); | ||
564 | if (!ctlr) | ||
565 | goto out; | ||
566 | |||
567 | ctlr->id = atomic_inc_return(&ctlr_num) - 1; | ||
568 | ctlr->f = f; | ||
569 | INIT_LIST_HEAD(&ctlr->fcfs); | ||
570 | mutex_init(&ctlr->lock); | ||
571 | ctlr->dev.parent = parent; | ||
572 | ctlr->dev.bus = &fcoe_bus_type; | ||
573 | ctlr->dev.type = &fcoe_ctlr_device_type; | ||
574 | |||
575 | ctlr->fcf_dev_loss_tmo = fcoe_fcf_dev_loss_tmo; | ||
576 | |||
577 | snprintf(ctlr->work_q_name, sizeof(ctlr->work_q_name), | ||
578 | "ctlr_wq_%d", ctlr->id); | ||
579 | ctlr->work_q = create_singlethread_workqueue( | ||
580 | ctlr->work_q_name); | ||
581 | if (!ctlr->work_q) | ||
582 | goto out_del; | ||
583 | |||
584 | snprintf(ctlr->devloss_work_q_name, | ||
585 | sizeof(ctlr->devloss_work_q_name), | ||
586 | "ctlr_dl_wq_%d", ctlr->id); | ||
587 | ctlr->devloss_work_q = create_singlethread_workqueue( | ||
588 | ctlr->devloss_work_q_name); | ||
589 | if (!ctlr->devloss_work_q) | ||
590 | goto out_del_q; | ||
591 | |||
592 | dev_set_name(&ctlr->dev, "ctlr_%d", ctlr->id); | ||
593 | error = device_register(&ctlr->dev); | ||
594 | if (error) | ||
595 | goto out_del_q2; | ||
596 | |||
597 | return ctlr; | ||
598 | |||
599 | out_del_q2: | ||
600 | destroy_workqueue(ctlr->devloss_work_q); | ||
601 | ctlr->devloss_work_q = NULL; | ||
602 | out_del_q: | ||
603 | destroy_workqueue(ctlr->work_q); | ||
604 | ctlr->work_q = NULL; | ||
605 | out_del: | ||
606 | kfree(ctlr); | ||
607 | out: | ||
608 | return NULL; | ||
609 | } | ||
610 | EXPORT_SYMBOL_GPL(fcoe_ctlr_device_add); | ||
611 | |||
612 | /** | ||
613 | * fcoe_ctlr_device_delete() - Delete a FIP ctlr and its subtree from sysfs | ||
614 | * @ctlr: A pointer to the ctlr to be deleted | ||
615 | * | ||
616 | * Deletes a FIP ctlr and any fcfs attached | ||
617 | * to it. Deleting fcfs will cause their childen | ||
618 | * to be deleted as well. | ||
619 | * | ||
620 | * The ctlr is detached from sysfs and it's resources | ||
621 | * are freed (work q), but the memory is not freed | ||
622 | * until its last reference is released. | ||
623 | * | ||
624 | * This routine expects no locks to be held before | ||
625 | * calling. | ||
626 | * | ||
627 | * TODO: Currently there are no callbacks to clean up LLD data | ||
628 | * for a fcoe_fcf_device. LLDs must keep this in mind as they need | ||
629 | * to clean up each of their LLD data for all fcoe_fcf_device before | ||
630 | * calling fcoe_ctlr_device_delete. | ||
631 | */ | ||
632 | void fcoe_ctlr_device_delete(struct fcoe_ctlr_device *ctlr) | ||
633 | { | ||
634 | struct fcoe_fcf_device *fcf, *next; | ||
635 | /* Remove any attached fcfs */ | ||
636 | mutex_lock(&ctlr->lock); | ||
637 | list_for_each_entry_safe(fcf, next, | ||
638 | &ctlr->fcfs, peers) { | ||
639 | list_del(&fcf->peers); | ||
640 | fcf->state = FCOE_FCF_STATE_DELETED; | ||
641 | fcoe_ctlr_device_queue_work(ctlr, &fcf->delete_work); | ||
642 | } | ||
643 | mutex_unlock(&ctlr->lock); | ||
644 | |||
645 | fcoe_ctlr_device_flush_work(ctlr); | ||
646 | |||
647 | destroy_workqueue(ctlr->devloss_work_q); | ||
648 | ctlr->devloss_work_q = NULL; | ||
649 | destroy_workqueue(ctlr->work_q); | ||
650 | ctlr->work_q = NULL; | ||
651 | |||
652 | device_unregister(&ctlr->dev); | ||
653 | } | ||
654 | EXPORT_SYMBOL_GPL(fcoe_ctlr_device_delete); | ||
655 | |||
656 | /** | ||
657 | * fcoe_fcf_device_final_delete() - Final delete routine | ||
658 | * @work: The FIP fcf's embedded work struct | ||
659 | * | ||
660 | * It is expected that the fcf has been removed from | ||
661 | * the FIP ctlr's list before calling this routine. | ||
662 | */ | ||
663 | static void fcoe_fcf_device_final_delete(struct work_struct *work) | ||
664 | { | ||
665 | struct fcoe_fcf_device *fcf = | ||
666 | container_of(work, struct fcoe_fcf_device, delete_work); | ||
667 | struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf); | ||
668 | |||
669 | /* | ||
670 | * Cancel any outstanding timers. These should really exist | ||
671 | * only when rmmod'ing the LLDD and we're asking for | ||
672 | * immediate termination of the rports | ||
673 | */ | ||
674 | if (!cancel_delayed_work(&fcf->dev_loss_work)) | ||
675 | fcoe_ctlr_device_flush_devloss(ctlr); | ||
676 | |||
677 | device_unregister(&fcf->dev); | ||
678 | } | ||
679 | |||
680 | /** | ||
681 | * fip_timeout_deleted_fcf() - Delete a fcf when the devloss timer fires | ||
682 | * @work: The FIP fcf's embedded work struct | ||
683 | * | ||
684 | * Removes the fcf from the FIP ctlr's list of fcfs and | ||
685 | * queues the final deletion. | ||
686 | */ | ||
687 | static void fip_timeout_deleted_fcf(struct work_struct *work) | ||
688 | { | ||
689 | struct fcoe_fcf_device *fcf = | ||
690 | container_of(work, struct fcoe_fcf_device, dev_loss_work.work); | ||
691 | struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf); | ||
692 | |||
693 | mutex_lock(&ctlr->lock); | ||
694 | |||
695 | /* | ||
696 | * If the fcf is deleted or reconnected before the timer | ||
697 | * fires the devloss queue will be flushed, but the state will | ||
698 | * either be CONNECTED or DELETED. If that is the case we | ||
699 | * cancel deleting the fcf. | ||
700 | */ | ||
701 | if (fcf->state != FCOE_FCF_STATE_DISCONNECTED) | ||
702 | goto out; | ||
703 | |||
704 | dev_printk(KERN_ERR, &fcf->dev, | ||
705 | "FIP fcf connection time out: removing fcf\n"); | ||
706 | |||
707 | list_del(&fcf->peers); | ||
708 | fcf->state = FCOE_FCF_STATE_DELETED; | ||
709 | fcoe_ctlr_device_queue_work(ctlr, &fcf->delete_work); | ||
710 | |||
711 | out: | ||
712 | mutex_unlock(&ctlr->lock); | ||
713 | } | ||
714 | |||
715 | /** | ||
716 | * fcoe_fcf_device_delete() - Delete a FIP fcf | ||
717 | * @fcf: Pointer to the fcf which is to be deleted | ||
718 | * | ||
719 | * Queues the FIP fcf on the devloss workqueue | ||
720 | * | ||
721 | * Expects the ctlr_attrs mutex to be held for fcf | ||
722 | * state change. | ||
723 | */ | ||
724 | void fcoe_fcf_device_delete(struct fcoe_fcf_device *fcf) | ||
725 | { | ||
726 | struct fcoe_ctlr_device *ctlr = fcoe_fcf_dev_to_ctlr_dev(fcf); | ||
727 | int timeout = fcf->dev_loss_tmo; | ||
728 | |||
729 | if (fcf->state != FCOE_FCF_STATE_CONNECTED) | ||
730 | return; | ||
731 | |||
732 | fcf->state = FCOE_FCF_STATE_DISCONNECTED; | ||
733 | |||
734 | /* | ||
735 | * FCF will only be re-connected by the LLD calling | ||
736 | * fcoe_fcf_device_add, and it should be setting up | ||
737 | * priv then. | ||
738 | */ | ||
739 | fcf->priv = NULL; | ||
740 | |||
741 | fcoe_ctlr_device_queue_devloss_work(ctlr, &fcf->dev_loss_work, | ||
742 | timeout * HZ); | ||
743 | } | ||
744 | EXPORT_SYMBOL_GPL(fcoe_fcf_device_delete); | ||
745 | |||
746 | /** | ||
747 | * fcoe_fcf_device_add() - Add a FCoE sysfs fcoe_fcf_device to the system | ||
748 | * @ctlr: The fcoe_ctlr_device that will be the fcoe_fcf_device parent | ||
749 | * @new_fcf: A temporary FCF used for lookups on the current list of fcfs | ||
750 | * | ||
751 | * Expects to be called with the ctlr->lock held | ||
752 | */ | ||
753 | struct fcoe_fcf_device *fcoe_fcf_device_add(struct fcoe_ctlr_device *ctlr, | ||
754 | struct fcoe_fcf_device *new_fcf) | ||
755 | { | ||
756 | struct fcoe_fcf_device *fcf; | ||
757 | int error = 0; | ||
758 | |||
759 | list_for_each_entry(fcf, &ctlr->fcfs, peers) { | ||
760 | if (fcoe_fcf_device_match(new_fcf, fcf)) { | ||
761 | if (fcf->state == FCOE_FCF_STATE_CONNECTED) | ||
762 | return fcf; | ||
763 | |||
764 | fcf->state = FCOE_FCF_STATE_CONNECTED; | ||
765 | |||
766 | if (!cancel_delayed_work(&fcf->dev_loss_work)) | ||
767 | fcoe_ctlr_device_flush_devloss(ctlr); | ||
768 | |||
769 | return fcf; | ||
770 | } | ||
771 | } | ||
772 | |||
773 | fcf = kzalloc(sizeof(struct fcoe_fcf_device), GFP_ATOMIC); | ||
774 | if (unlikely(!fcf)) | ||
775 | goto out; | ||
776 | |||
777 | INIT_WORK(&fcf->delete_work, fcoe_fcf_device_final_delete); | ||
778 | INIT_DELAYED_WORK(&fcf->dev_loss_work, fip_timeout_deleted_fcf); | ||
779 | |||
780 | fcf->dev.parent = &ctlr->dev; | ||
781 | fcf->dev.bus = &fcoe_bus_type; | ||
782 | fcf->dev.type = &fcoe_fcf_device_type; | ||
783 | fcf->id = atomic_inc_return(&fcf_num) - 1; | ||
784 | fcf->state = FCOE_FCF_STATE_UNKNOWN; | ||
785 | |||
786 | fcf->dev_loss_tmo = ctlr->fcf_dev_loss_tmo; | ||
787 | |||
788 | dev_set_name(&fcf->dev, "fcf_%d", fcf->id); | ||
789 | |||
790 | fcf->fabric_name = new_fcf->fabric_name; | ||
791 | fcf->switch_name = new_fcf->switch_name; | ||
792 | fcf->fc_map = new_fcf->fc_map; | ||
793 | fcf->vfid = new_fcf->vfid; | ||
794 | memcpy(fcf->mac, new_fcf->mac, ETH_ALEN); | ||
795 | fcf->priority = new_fcf->priority; | ||
796 | fcf->fka_period = new_fcf->fka_period; | ||
797 | fcf->selected = new_fcf->selected; | ||
798 | |||
799 | error = device_register(&fcf->dev); | ||
800 | if (error) | ||
801 | goto out_del; | ||
802 | |||
803 | fcf->state = FCOE_FCF_STATE_CONNECTED; | ||
804 | list_add_tail(&fcf->peers, &ctlr->fcfs); | ||
805 | |||
806 | return fcf; | ||
807 | |||
808 | out_del: | ||
809 | kfree(fcf); | ||
810 | out: | ||
811 | return NULL; | ||
812 | } | ||
813 | EXPORT_SYMBOL_GPL(fcoe_fcf_device_add); | ||
814 | |||
815 | int __init fcoe_sysfs_setup(void) | ||
816 | { | ||
817 | int error; | ||
818 | |||
819 | atomic_set(&ctlr_num, 0); | ||
820 | atomic_set(&fcf_num, 0); | ||
821 | |||
822 | error = bus_register(&fcoe_bus_type); | ||
823 | if (error) | ||
824 | return error; | ||
825 | |||
826 | return 0; | ||
827 | } | ||
828 | |||
829 | void __exit fcoe_sysfs_teardown(void) | ||
830 | { | ||
831 | bus_unregister(&fcoe_bus_type); | ||
832 | } | ||