aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/fcoe
diff options
context:
space:
mode:
authorRobert Love <robert.w.love@intel.com>2012-05-22 22:06:21 -0400
committerJames Bottomley <JBottomley@Parallels.com>2012-05-23 04:40:09 -0400
commit9a74e884ee71dbf3d0967b0321d7b4529a04826c (patch)
tree29cf0211927a3e54908f2dffa9ebde18794d6ed6 /drivers/scsi/fcoe
parentfd8f89027d816cb023edf6bfd4c744f194150a05 (diff)
[SCSI] libfcoe: Add fcoe_sysfs
This patch adds a 'fcoe bus' infrastructure to the kernel that is driven by changes to libfcoe which allow LLDs to present FIP (FCoE Initialization Protocol) discovered entities and their attributes to user space via sysfs. This patch adds the following APIs- fcoe_ctlr_device_add fcoe_ctlr_device_delete fcoe_fcf_device_add fcoe_fcf_device_delete They allow the LLD to expose the FCoE ENode Controller and any discovered FCFs (Fibre Channel Forwarders, e.g. FCoE switches) to the user. Each of these new devices has their own bus_type so that they are grouped together for easy lookup from a user space application. Each new class has an attribute_group to expose attributes for any created instances. The attributes are- fcoe_ctlr_device * fcf_dev_loss_tmo * lesb_link_fail * lesb_vlink_fail * lesb_miss_fka * lesb_symb_err * lesb_err_block * lesb_fcs_error fcoe_fcf_device * fabric_name * switch_name * priority * selected * fc_map * vfid * mac * fka_peroid * fabric_state * dev_loss_tmo A device loss infrastructre similar to the FC Transport's is also added by this patch. It is nice to have so that a link flapping adapter doesn't continually advance the count used to identify the discovered FCF. FCFs will exist in a "Disconnected" state until either the timer expires or the FCF is rediscovered and becomes "Connected." This patch generates a few checkpatch.pl WARNINGS that I'm not sure what to do about. They're macros modeled around the FC Transport attribute building macros, which have the same 'feature' where the caller can ommit a cast in the argument list and no cast occurs in the code. I'm not sure how to keep the code condensed while keeping the macros. Any advice would be appreciated. Signed-off-by: Robert Love <robert.w.love@intel.com> Tested-by: Ross Brattain <ross.b.brattain@intel.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/fcoe')
-rw-r--r--drivers/scsi/fcoe/Makefile2
-rw-r--r--drivers/scsi/fcoe/fcoe_sysfs.c832
-rw-r--r--drivers/scsi/fcoe/fcoe_transport.c13
3 files changed, 844 insertions, 3 deletions
diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index f6d37d0271f7..aed0f5db3668 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -1,4 +1,4 @@
1obj-$(CONFIG_FCOE) += fcoe.o 1obj-$(CONFIG_FCOE) += fcoe.o
2obj-$(CONFIG_LIBFCOE) += libfcoe.o 2obj-$(CONFIG_LIBFCOE) += libfcoe.o
3 3
4libfcoe-objs := fcoe_ctlr.o fcoe_transport.o 4libfcoe-objs := fcoe_ctlr.o fcoe_transport.o fcoe_sysfs.o
diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c
new file mode 100644
index 000000000000..2bc163198d33
--- /dev/null
+++ b/drivers/scsi/fcoe/fcoe_sysfs.c
@@ -0,0 +1,832 @@
1/*
2 * Copyright(c) 2011 - 2012 Intel Corporation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
16 *
17 * Maintained at www.Open-FCoE.org
18 */
19
20#include <linux/module.h>
21#include <linux/types.h>
22#include <linux/kernel.h>
23#include <linux/etherdevice.h>
24
25#include <scsi/fcoe_sysfs.h>
26
27static atomic_t ctlr_num;
28static 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 */
34static unsigned int fcoe_fcf_dev_loss_tmo = 1800; /* seconds */
35
36module_param_named(fcf_dev_loss_tmo, fcoe_fcf_dev_loss_tmo,
37 uint, S_IRUGO|S_IWUSR);
38MODULE_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 */
100static 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
116static 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) \
134struct 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) \
138static 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) \
150static 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) \
163static 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) \
172static 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) \
211static 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
225static 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};
233fcoe_enum_name_search(fcf_state, fcf_state, fcf_state_names)
234#define FCOE_FCF_STATE_MAX_NAMELEN 50
235
236static 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}
247static FCOE_DEVICE_ATTR(fcf, state, S_IRUGO, show_fcf_state, NULL);
248
249static 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};
257fcoe_enum_name_search(ctlr_mode, fip_conn_type, fip_conn_type_names)
258#define FCOE_CTLR_MODE_MAX_NAMELEN 50
259
260static 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}
276static FCOE_DEVICE_ATTR(ctlr, mode, S_IRUGO,
277 show_ctlr_mode, NULL);
278
279static ssize_t
280store_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}
300fcoe_ctlr_private_show_function(fcf_dev_loss_tmo, "%d\n", 20, );
301static 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) */
306fcoe_ctlr_rd_attr(link_fail, "%u\n", 20);
307fcoe_ctlr_rd_attr(vlink_fail, "%u\n", 20);
308fcoe_ctlr_rd_attr(miss_fka, "%u\n", 20);
309fcoe_ctlr_rd_attr(symb_err, "%u\n", 20);
310fcoe_ctlr_rd_attr(err_block, "%u\n", 20);
311fcoe_ctlr_rd_attr(fcs_error, "%u\n", 20);
312
313fcoe_fcf_private_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long);
314fcoe_fcf_private_rd_attr_cast(switch_name, "0x%llx\n", 20, unsigned long long);
315fcoe_fcf_private_rd_attr(priority, "%u\n", 20);
316fcoe_fcf_private_rd_attr(fc_map, "0x%x\n", 20);
317fcoe_fcf_private_rd_attr(vfid, "%u\n", 20);
318fcoe_fcf_private_rd_attr(mac, "%pM\n", 20);
319fcoe_fcf_private_rd_attr(fka_period, "%u\n", 20);
320fcoe_fcf_rd_attr(selected, "%u\n", 20);
321fcoe_fcf_rd_attr(vlan_id, "%u\n", 20);
322
323fcoe_fcf_private_show_function(dev_loss_tmo, "%d\n", 20, )
324static ssize_t
325store_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}
341static 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
345static 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
355static struct attribute_group fcoe_ctlr_lesb_attr_group = {
356 .name = "lesb",
357 .attrs = fcoe_ctlr_lesb_attrs,
358};
359
360static 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
366static struct attribute_group fcoe_ctlr_attr_group = {
367 .attrs = fcoe_ctlr_attrs,
368};
369
370static const struct attribute_group *fcoe_ctlr_attr_groups[] = {
371 &fcoe_ctlr_attr_group,
372 &fcoe_ctlr_lesb_attr_group,
373 NULL,
374};
375
376static 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
391static struct attribute_group fcoe_fcf_attr_group = {
392 .attrs = fcoe_fcf_attrs,
393};
394
395static const struct attribute_group *fcoe_fcf_attr_groups[] = {
396 &fcoe_fcf_attr_group,
397 NULL,
398};
399
400struct bus_type fcoe_bus_type;
401
402static 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 */
416static 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 */
428static void fcoe_fcf_device_release(struct device *dev)
429{
430 struct fcoe_fcf_device *fcf = dev_to_fcf(dev);
431 kfree(fcf);
432}
433
434struct device_type fcoe_ctlr_device_type = {
435 .name = "fcoe_ctlr",
436 .groups = fcoe_ctlr_attr_groups,
437 .release = fcoe_ctlr_device_release,
438};
439
440struct device_type fcoe_fcf_device_type = {
441 .name = "fcoe_fcf",
442 .groups = fcoe_fcf_attr_groups,
443 .release = fcoe_fcf_device_release,
444};
445
446struct 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 */
455void 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 */
476int 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 */
495void 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 */
517int 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
533static 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 */
555struct 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
599out_del_q2:
600 destroy_workqueue(ctlr->devloss_work_q);
601 ctlr->devloss_work_q = NULL;
602out_del_q:
603 destroy_workqueue(ctlr->work_q);
604 ctlr->work_q = NULL;
605out_del:
606 kfree(ctlr);
607out:
608 return NULL;
609}
610EXPORT_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 */
632void 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}
654EXPORT_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 */
663static 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 */
687static 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
711out:
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 */
724void 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}
744EXPORT_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 */
753struct 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
808out_del:
809 kfree(fcf);
810out:
811 return NULL;
812}
813EXPORT_SYMBOL_GPL(fcoe_fcf_device_add);
814
815int __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
829void __exit fcoe_sysfs_teardown(void)
830{
831 bus_unregister(&fcoe_bus_type);
832}
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
index 710e149d41b6..b46f43dced78 100644
--- a/drivers/scsi/fcoe/fcoe_transport.c
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -815,9 +815,17 @@ out_nodev:
815 */ 815 */
816static int __init libfcoe_init(void) 816static int __init libfcoe_init(void)
817{ 817{
818 fcoe_transport_init(); 818 int rc = 0;
819 819
820 return 0; 820 rc = fcoe_transport_init();
821 if (rc)
822 return rc;
823
824 rc = fcoe_sysfs_setup();
825 if (rc)
826 fcoe_transport_exit();
827
828 return rc;
821} 829}
822module_init(libfcoe_init); 830module_init(libfcoe_init);
823 831
@@ -826,6 +834,7 @@ module_init(libfcoe_init);
826 */ 834 */
827static void __exit libfcoe_exit(void) 835static void __exit libfcoe_exit(void)
828{ 836{
837 fcoe_sysfs_teardown();
829 fcoe_transport_exit(); 838 fcoe_transport_exit();
830} 839}
831module_exit(libfcoe_exit); 840module_exit(libfcoe_exit);