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