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