Commit | Line | Data |
---|---|---|
9a74e884 RL |
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 | } |