diff options
author | Robert Love <robert.w.love@intel.com> | 2008-12-09 18:10:24 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-12-29 12:24:33 -0500 |
commit | 85b4aa4926a50210b683ac89326e338e7d131211 (patch) | |
tree | 127b6586573194f2d37b5a298e12c8b7d24a6fac | |
parent | 42e9a92fe6a9095bd68a379aaec7ad2be0337f7a (diff) |
[SCSI] fcoe: Fibre Channel over Ethernet
Encapsulation protocol for running Fibre Channel over Ethernet interfaces.
Creates virtual Fibre Channel host adapters using libfc.
This layer is the LLD to the scsi-ml. It allocates the Scsi_Host, utilizes
libfc for Fibre Channel protocol processing and interacts with netdev to
send/receive Ethernet packets.
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r-- | drivers/scsi/Kconfig | 7 | ||||
-rw-r--r-- | drivers/scsi/Makefile | 1 | ||||
-rw-r--r-- | drivers/scsi/fcoe/Makefile | 8 | ||||
-rw-r--r-- | drivers/scsi/fcoe/fc_transport_fcoe.c | 446 | ||||
-rw-r--r-- | drivers/scsi/fcoe/fcoe_sw.c | 494 | ||||
-rw-r--r-- | drivers/scsi/fcoe/libfcoe.c | 1510 | ||||
-rw-r--r-- | include/scsi/fc_transport_fcoe.h | 54 | ||||
-rw-r--r-- | include/scsi/libfcoe.h | 176 |
8 files changed, 2696 insertions, 0 deletions
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 24d762aab7c5..673463e4bbf0 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig | |||
@@ -609,6 +609,13 @@ config LIBFC | |||
609 | ---help--- | 609 | ---help--- |
610 | Fibre Channel library module | 610 | Fibre Channel library module |
611 | 611 | ||
612 | config FCOE | ||
613 | tristate "FCoE module" | ||
614 | depends on SCSI | ||
615 | select LIBFC | ||
616 | ---help--- | ||
617 | Fibre Channel over Ethernet module | ||
618 | |||
612 | config SCSI_DMX3191D | 619 | config SCSI_DMX3191D |
613 | tristate "DMX3191D SCSI support" | 620 | tristate "DMX3191D SCSI support" |
614 | depends on PCI && SCSI | 621 | depends on PCI && SCSI |
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 87355f573d60..07d0f58de9bb 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile | |||
@@ -37,6 +37,7 @@ obj-$(CONFIG_SCSI_SRP_ATTRS) += scsi_transport_srp.o | |||
37 | obj-$(CONFIG_SCSI_DH) += device_handler/ | 37 | obj-$(CONFIG_SCSI_DH) += device_handler/ |
38 | 38 | ||
39 | obj-$(CONFIG_LIBFC) += libfc/ | 39 | obj-$(CONFIG_LIBFC) += libfc/ |
40 | obj-$(CONFIG_FCOE) += fcoe/ | ||
40 | obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o | 41 | obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o |
41 | obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o | 42 | obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o |
42 | obj-$(CONFIG_SCSI_A4000T) += 53c700.o a4000t.o | 43 | obj-$(CONFIG_SCSI_A4000T) += 53c700.o a4000t.o |
diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile new file mode 100644 index 000000000000..b78da06d7c0e --- /dev/null +++ b/drivers/scsi/fcoe/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | # $Id: Makefile | ||
2 | |||
3 | obj-$(CONFIG_FCOE) += fcoe.o | ||
4 | |||
5 | fcoe-y := \ | ||
6 | libfcoe.o \ | ||
7 | fcoe_sw.o \ | ||
8 | fc_transport_fcoe.o | ||
diff --git a/drivers/scsi/fcoe/fc_transport_fcoe.c b/drivers/scsi/fcoe/fc_transport_fcoe.c new file mode 100644 index 000000000000..bf7fe6fc0820 --- /dev/null +++ b/drivers/scsi/fcoe/fc_transport_fcoe.c | |||
@@ -0,0 +1,446 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2007 - 2008 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/pci.h> | ||
21 | #include <scsi/libfcoe.h> | ||
22 | #include <scsi/fc_transport_fcoe.h> | ||
23 | |||
24 | /* internal fcoe transport */ | ||
25 | struct fcoe_transport_internal { | ||
26 | struct fcoe_transport *t; | ||
27 | struct net_device *netdev; | ||
28 | struct list_head list; | ||
29 | }; | ||
30 | |||
31 | /* fcoe transports list and its lock */ | ||
32 | static LIST_HEAD(fcoe_transports); | ||
33 | static DEFINE_MUTEX(fcoe_transports_lock); | ||
34 | |||
35 | /** | ||
36 | * fcoe_transport_default - returns ptr to the default transport fcoe_sw | ||
37 | **/ | ||
38 | struct fcoe_transport *fcoe_transport_default(void) | ||
39 | { | ||
40 | return &fcoe_sw_transport; | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * fcoe_transport_to_pcidev - get the pci dev from a netdev | ||
45 | * @netdev: the netdev that pci dev will be retrived from | ||
46 | * | ||
47 | * Returns: NULL or the corrsponding pci_dev | ||
48 | **/ | ||
49 | struct pci_dev *fcoe_transport_pcidev(const struct net_device *netdev) | ||
50 | { | ||
51 | if (!netdev->dev.parent) | ||
52 | return NULL; | ||
53 | return to_pci_dev(netdev->dev.parent); | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * fcoe_transport_device_lookup - find out netdev is managed by the | ||
58 | * transport | ||
59 | * assign a transport to a device | ||
60 | * @netdev: the netdev the transport to be attached to | ||
61 | * | ||
62 | * This will look for existing offload driver, if not found, it falls back to | ||
63 | * the default sw hba (fcoe_sw) as its fcoe transport. | ||
64 | * | ||
65 | * Returns: 0 for success | ||
66 | **/ | ||
67 | static struct fcoe_transport_internal *fcoe_transport_device_lookup( | ||
68 | struct fcoe_transport *t, struct net_device *netdev) | ||
69 | { | ||
70 | struct fcoe_transport_internal *ti; | ||
71 | |||
72 | /* assign the transpor to this device */ | ||
73 | mutex_lock(&t->devlock); | ||
74 | list_for_each_entry(ti, &t->devlist, list) { | ||
75 | if (ti->netdev == netdev) { | ||
76 | mutex_unlock(&t->devlock); | ||
77 | return ti; | ||
78 | } | ||
79 | } | ||
80 | mutex_unlock(&t->devlock); | ||
81 | return NULL; | ||
82 | } | ||
83 | /** | ||
84 | * fcoe_transport_device_add - assign a transport to a device | ||
85 | * @netdev: the netdev the transport to be attached to | ||
86 | * | ||
87 | * This will look for existing offload driver, if not found, it falls back to | ||
88 | * the default sw hba (fcoe_sw) as its fcoe transport. | ||
89 | * | ||
90 | * Returns: 0 for success | ||
91 | **/ | ||
92 | static int fcoe_transport_device_add(struct fcoe_transport *t, | ||
93 | struct net_device *netdev) | ||
94 | { | ||
95 | struct fcoe_transport_internal *ti; | ||
96 | |||
97 | ti = fcoe_transport_device_lookup(t, netdev); | ||
98 | if (ti) { | ||
99 | printk(KERN_DEBUG "fcoe_transport_device_add:" | ||
100 | "device %s is already added to transport %s\n", | ||
101 | netdev->name, t->name); | ||
102 | return -EEXIST; | ||
103 | } | ||
104 | /* allocate an internal struct to host the netdev and the list */ | ||
105 | ti = kzalloc(sizeof(*ti), GFP_KERNEL); | ||
106 | if (!ti) | ||
107 | return -ENOMEM; | ||
108 | |||
109 | ti->t = t; | ||
110 | ti->netdev = netdev; | ||
111 | INIT_LIST_HEAD(&ti->list); | ||
112 | dev_hold(ti->netdev); | ||
113 | |||
114 | mutex_lock(&t->devlock); | ||
115 | list_add(&ti->list, &t->devlist); | ||
116 | mutex_unlock(&t->devlock); | ||
117 | |||
118 | printk(KERN_DEBUG "fcoe_transport_device_add:" | ||
119 | "device %s added to transport %s\n", | ||
120 | netdev->name, t->name); | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * fcoe_transport_device_remove - remove a device from its transport | ||
127 | * @netdev: the netdev the transport to be attached to | ||
128 | * | ||
129 | * this removes the device from the transport so the given transport will | ||
130 | * not manage this device any more | ||
131 | * | ||
132 | * Returns: 0 for success | ||
133 | **/ | ||
134 | static int fcoe_transport_device_remove(struct fcoe_transport *t, | ||
135 | struct net_device *netdev) | ||
136 | { | ||
137 | struct fcoe_transport_internal *ti; | ||
138 | |||
139 | ti = fcoe_transport_device_lookup(t, netdev); | ||
140 | if (!ti) { | ||
141 | printk(KERN_DEBUG "fcoe_transport_device_remove:" | ||
142 | "device %s is not managed by transport %s\n", | ||
143 | netdev->name, t->name); | ||
144 | return -ENODEV; | ||
145 | } | ||
146 | mutex_lock(&t->devlock); | ||
147 | list_del(&ti->list); | ||
148 | mutex_unlock(&t->devlock); | ||
149 | printk(KERN_DEBUG "fcoe_transport_device_remove:" | ||
150 | "device %s removed from transport %s\n", | ||
151 | netdev->name, t->name); | ||
152 | dev_put(ti->netdev); | ||
153 | kfree(ti); | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | /** | ||
158 | * fcoe_transport_device_remove_all - remove all from transport devlist | ||
159 | * | ||
160 | * this removes the device from the transport so the given transport will | ||
161 | * not manage this device any more | ||
162 | * | ||
163 | * Returns: 0 for success | ||
164 | **/ | ||
165 | static void fcoe_transport_device_remove_all(struct fcoe_transport *t) | ||
166 | { | ||
167 | struct fcoe_transport_internal *ti, *tmp; | ||
168 | |||
169 | mutex_lock(&t->devlock); | ||
170 | list_for_each_entry_safe(ti, tmp, &t->devlist, list) { | ||
171 | list_del(&ti->list); | ||
172 | kfree(ti); | ||
173 | } | ||
174 | mutex_unlock(&t->devlock); | ||
175 | } | ||
176 | |||
177 | /** | ||
178 | * fcoe_transport_match - use the bus device match function to match the hw | ||
179 | * @t: the fcoe transport | ||
180 | * @netdev: | ||
181 | * | ||
182 | * This function is used to check if the givne transport wants to manage the | ||
183 | * input netdev. if the transports implements the match function, it will be | ||
184 | * called, o.w. we just compare the pci vendor and device id. | ||
185 | * | ||
186 | * Returns: true for match up | ||
187 | **/ | ||
188 | static bool fcoe_transport_match(struct fcoe_transport *t, | ||
189 | struct net_device *netdev) | ||
190 | { | ||
191 | /* match transport by vendor and device id */ | ||
192 | struct pci_dev *pci; | ||
193 | |||
194 | pci = fcoe_transport_pcidev(netdev); | ||
195 | |||
196 | if (pci) { | ||
197 | printk(KERN_DEBUG "fcoe_transport_match:" | ||
198 | "%s:%x:%x -- %s:%x:%x\n", | ||
199 | t->name, t->vendor, t->device, | ||
200 | netdev->name, pci->vendor, pci->device); | ||
201 | |||
202 | /* if transport supports match */ | ||
203 | if (t->match) | ||
204 | return t->match(netdev); | ||
205 | |||
206 | /* else just compare the vendor and device id: pci only */ | ||
207 | return (t->vendor == pci->vendor) && (t->device == pci->device); | ||
208 | } | ||
209 | return false; | ||
210 | } | ||
211 | |||
212 | /** | ||
213 | * fcoe_transport_lookup - check if the transport is already registered | ||
214 | * @t: the transport to be looked up | ||
215 | * | ||
216 | * This compares the parent device (pci) vendor and device id | ||
217 | * | ||
218 | * Returns: NULL if not found | ||
219 | * | ||
220 | * TODO - return default sw transport if no other transport is found | ||
221 | **/ | ||
222 | static struct fcoe_transport *fcoe_transport_lookup( | ||
223 | struct net_device *netdev) | ||
224 | { | ||
225 | struct fcoe_transport *t; | ||
226 | |||
227 | mutex_lock(&fcoe_transports_lock); | ||
228 | list_for_each_entry(t, &fcoe_transports, list) { | ||
229 | if (fcoe_transport_match(t, netdev)) { | ||
230 | mutex_unlock(&fcoe_transports_lock); | ||
231 | return t; | ||
232 | } | ||
233 | } | ||
234 | mutex_unlock(&fcoe_transports_lock); | ||
235 | |||
236 | printk(KERN_DEBUG "fcoe_transport_lookup:" | ||
237 | "use default transport for %s\n", netdev->name); | ||
238 | return fcoe_transport_default(); | ||
239 | } | ||
240 | |||
241 | /** | ||
242 | * fcoe_transport_register - adds a fcoe transport to the fcoe transports list | ||
243 | * @t: ptr to the fcoe transport to be added | ||
244 | * | ||
245 | * Returns: 0 for success | ||
246 | **/ | ||
247 | int fcoe_transport_register(struct fcoe_transport *t) | ||
248 | { | ||
249 | struct fcoe_transport *tt; | ||
250 | |||
251 | /* TODO - add fcoe_transport specific initialization here */ | ||
252 | mutex_lock(&fcoe_transports_lock); | ||
253 | list_for_each_entry(tt, &fcoe_transports, list) { | ||
254 | if (tt == t) { | ||
255 | mutex_unlock(&fcoe_transports_lock); | ||
256 | return -EEXIST; | ||
257 | } | ||
258 | } | ||
259 | list_add_tail(&t->list, &fcoe_transports); | ||
260 | mutex_unlock(&fcoe_transports_lock); | ||
261 | |||
262 | mutex_init(&t->devlock); | ||
263 | INIT_LIST_HEAD(&t->devlist); | ||
264 | |||
265 | printk(KERN_DEBUG "fcoe_transport_register:%s\n", t->name); | ||
266 | |||
267 | return 0; | ||
268 | } | ||
269 | EXPORT_SYMBOL_GPL(fcoe_transport_register); | ||
270 | |||
271 | /** | ||
272 | * fcoe_transport_unregister - remove the tranport fro the fcoe transports list | ||
273 | * @t: ptr to the fcoe transport to be removed | ||
274 | * | ||
275 | * Returns: 0 for success | ||
276 | **/ | ||
277 | int fcoe_transport_unregister(struct fcoe_transport *t) | ||
278 | { | ||
279 | struct fcoe_transport *tt, *tmp; | ||
280 | |||
281 | mutex_lock(&fcoe_transports_lock); | ||
282 | list_for_each_entry_safe(tt, tmp, &fcoe_transports, list) { | ||
283 | if (tt == t) { | ||
284 | list_del(&t->list); | ||
285 | mutex_unlock(&fcoe_transports_lock); | ||
286 | fcoe_transport_device_remove_all(t); | ||
287 | printk(KERN_DEBUG "fcoe_transport_unregister:%s\n", | ||
288 | t->name); | ||
289 | return 0; | ||
290 | } | ||
291 | } | ||
292 | mutex_unlock(&fcoe_transports_lock); | ||
293 | return -ENODEV; | ||
294 | } | ||
295 | EXPORT_SYMBOL_GPL(fcoe_transport_unregister); | ||
296 | |||
297 | /* | ||
298 | * fcoe_load_transport_driver - load an offload driver by alias name | ||
299 | * @netdev: the target net device | ||
300 | * | ||
301 | * Requests for an offload driver module as the fcoe transport, if fails, it | ||
302 | * falls back to use the SW HBA (fcoe_sw) as its transport | ||
303 | * | ||
304 | * TODO - | ||
305 | * 1. supports only PCI device | ||
306 | * 2. needs fix for VLAn and bonding | ||
307 | * 3. pure hw fcoe hba may not have netdev | ||
308 | * | ||
309 | * Returns: 0 for success | ||
310 | **/ | ||
311 | int fcoe_load_transport_driver(struct net_device *netdev) | ||
312 | { | ||
313 | struct pci_dev *pci; | ||
314 | struct device *dev = netdev->dev.parent; | ||
315 | |||
316 | if (fcoe_transport_lookup(netdev)) { | ||
317 | /* load default transport */ | ||
318 | printk(KERN_DEBUG "fcoe: already loaded transport for %s\n", | ||
319 | netdev->name); | ||
320 | return -EEXIST; | ||
321 | } | ||
322 | |||
323 | pci = to_pci_dev(dev); | ||
324 | if (dev->bus != &pci_bus_type) { | ||
325 | printk(KERN_DEBUG "fcoe: support noly PCI device\n"); | ||
326 | return -ENODEV; | ||
327 | } | ||
328 | printk(KERN_DEBUG "fcoe: loading driver fcoe-pci-0x%04x-0x%04x\n", | ||
329 | pci->vendor, pci->device); | ||
330 | |||
331 | return request_module("fcoe-pci-0x%04x-0x%04x", | ||
332 | pci->vendor, pci->device); | ||
333 | |||
334 | } | ||
335 | EXPORT_SYMBOL_GPL(fcoe_load_transport_driver); | ||
336 | |||
337 | /** | ||
338 | * fcoe_transport_attach - load transport to fcoe | ||
339 | * @netdev: the netdev the transport to be attached to | ||
340 | * | ||
341 | * This will look for existing offload driver, if not found, it falls back to | ||
342 | * the default sw hba (fcoe_sw) as its fcoe transport. | ||
343 | * | ||
344 | * Returns: 0 for success | ||
345 | **/ | ||
346 | int fcoe_transport_attach(struct net_device *netdev) | ||
347 | { | ||
348 | struct fcoe_transport *t; | ||
349 | |||
350 | /* find the corresponding transport */ | ||
351 | t = fcoe_transport_lookup(netdev); | ||
352 | if (!t) { | ||
353 | printk(KERN_DEBUG "fcoe_transport_attach" | ||
354 | ":no transport for %s:use %s\n", | ||
355 | netdev->name, t->name); | ||
356 | return -ENODEV; | ||
357 | } | ||
358 | /* add to the transport */ | ||
359 | if (fcoe_transport_device_add(t, netdev)) { | ||
360 | printk(KERN_DEBUG "fcoe_transport_attach" | ||
361 | ":failed to add %s to tramsport %s\n", | ||
362 | netdev->name, t->name); | ||
363 | return -EIO; | ||
364 | } | ||
365 | /* transport create function */ | ||
366 | if (t->create) | ||
367 | t->create(netdev); | ||
368 | |||
369 | printk(KERN_DEBUG "fcoe_transport_attach:transport %s for %s\n", | ||
370 | t->name, netdev->name); | ||
371 | return 0; | ||
372 | } | ||
373 | EXPORT_SYMBOL_GPL(fcoe_transport_attach); | ||
374 | |||
375 | /** | ||
376 | * fcoe_transport_release - unload transport from fcoe | ||
377 | * @netdev: the net device on which fcoe is to be released | ||
378 | * | ||
379 | * Returns: 0 for success | ||
380 | **/ | ||
381 | int fcoe_transport_release(struct net_device *netdev) | ||
382 | { | ||
383 | struct fcoe_transport *t; | ||
384 | |||
385 | /* find the corresponding transport */ | ||
386 | t = fcoe_transport_lookup(netdev); | ||
387 | if (!t) { | ||
388 | printk(KERN_DEBUG "fcoe_transport_release:" | ||
389 | "no transport for %s:use %s\n", | ||
390 | netdev->name, t->name); | ||
391 | return -ENODEV; | ||
392 | } | ||
393 | /* remove the device from the transport */ | ||
394 | if (fcoe_transport_device_remove(t, netdev)) { | ||
395 | printk(KERN_DEBUG "fcoe_transport_release:" | ||
396 | "failed to add %s to tramsport %s\n", | ||
397 | netdev->name, t->name); | ||
398 | return -EIO; | ||
399 | } | ||
400 | /* transport destroy function */ | ||
401 | if (t->destroy) | ||
402 | t->destroy(netdev); | ||
403 | |||
404 | printk(KERN_DEBUG "fcoe_transport_release:" | ||
405 | "device %s dettached from transport %s\n", | ||
406 | netdev->name, t->name); | ||
407 | |||
408 | return 0; | ||
409 | } | ||
410 | EXPORT_SYMBOL_GPL(fcoe_transport_release); | ||
411 | |||
412 | /** | ||
413 | * fcoe_transport_init - initializes fcoe transport layer | ||
414 | * | ||
415 | * This prepares for the fcoe transport layer | ||
416 | * | ||
417 | * Returns: none | ||
418 | **/ | ||
419 | int __init fcoe_transport_init(void) | ||
420 | { | ||
421 | INIT_LIST_HEAD(&fcoe_transports); | ||
422 | mutex_init(&fcoe_transports_lock); | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | /** | ||
427 | * fcoe_transport_exit - cleans up the fcoe transport layer | ||
428 | * This cleans up the fcoe transport layer. removing any transport on the list, | ||
429 | * note that the transport destroy func is not called here. | ||
430 | * | ||
431 | * Returns: none | ||
432 | **/ | ||
433 | int __exit fcoe_transport_exit(void) | ||
434 | { | ||
435 | struct fcoe_transport *t, *tmp; | ||
436 | |||
437 | mutex_lock(&fcoe_transports_lock); | ||
438 | list_for_each_entry_safe(t, tmp, &fcoe_transports, list) { | ||
439 | list_del(&t->list); | ||
440 | mutex_unlock(&fcoe_transports_lock); | ||
441 | fcoe_transport_device_remove_all(t); | ||
442 | mutex_lock(&fcoe_transports_lock); | ||
443 | } | ||
444 | mutex_unlock(&fcoe_transports_lock); | ||
445 | return 0; | ||
446 | } | ||
diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c new file mode 100644 index 000000000000..dc4cd5e25760 --- /dev/null +++ b/drivers/scsi/fcoe/fcoe_sw.c | |||
@@ -0,0 +1,494 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2007 - 2008 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/version.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/pci.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/spinlock.h> | ||
26 | #include <linux/netdevice.h> | ||
27 | #include <linux/etherdevice.h> | ||
28 | #include <linux/if_vlan.h> | ||
29 | #include <net/rtnetlink.h> | ||
30 | |||
31 | #include <scsi/fc/fc_els.h> | ||
32 | #include <scsi/fc/fc_encaps.h> | ||
33 | #include <scsi/fc/fc_fs.h> | ||
34 | #include <scsi/scsi_transport.h> | ||
35 | #include <scsi/scsi_transport_fc.h> | ||
36 | |||
37 | #include <scsi/libfc.h> | ||
38 | #include <scsi/libfcoe.h> | ||
39 | #include <scsi/fc_transport_fcoe.h> | ||
40 | |||
41 | #define FCOE_SW_VERSION "0.1" | ||
42 | #define FCOE_SW_NAME "fcoesw" | ||
43 | #define FCOE_SW_VENDOR "Open-FCoE.org" | ||
44 | |||
45 | #define FCOE_MAX_LUN 255 | ||
46 | #define FCOE_MAX_FCP_TARGET 256 | ||
47 | |||
48 | #define FCOE_MAX_OUTSTANDING_COMMANDS 1024 | ||
49 | |||
50 | #define FCOE_MIN_XID 0x0001 /* the min xid supported by fcoe_sw */ | ||
51 | #define FCOE_MAX_XID 0x07ef /* the max xid supported by fcoe_sw */ | ||
52 | |||
53 | static struct scsi_transport_template *scsi_transport_fcoe_sw; | ||
54 | |||
55 | struct fc_function_template fcoe_sw_transport_function = { | ||
56 | .show_host_node_name = 1, | ||
57 | .show_host_port_name = 1, | ||
58 | .show_host_supported_classes = 1, | ||
59 | .show_host_supported_fc4s = 1, | ||
60 | .show_host_active_fc4s = 1, | ||
61 | .show_host_maxframe_size = 1, | ||
62 | |||
63 | .show_host_port_id = 1, | ||
64 | .show_host_supported_speeds = 1, | ||
65 | .get_host_speed = fc_get_host_speed, | ||
66 | .show_host_speed = 1, | ||
67 | .show_host_port_type = 1, | ||
68 | .get_host_port_state = fc_get_host_port_state, | ||
69 | .show_host_port_state = 1, | ||
70 | .show_host_symbolic_name = 1, | ||
71 | |||
72 | .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv), | ||
73 | .show_rport_maxframe_size = 1, | ||
74 | .show_rport_supported_classes = 1, | ||
75 | |||
76 | .show_host_fabric_name = 1, | ||
77 | .show_starget_node_name = 1, | ||
78 | .show_starget_port_name = 1, | ||
79 | .show_starget_port_id = 1, | ||
80 | .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo, | ||
81 | .show_rport_dev_loss_tmo = 1, | ||
82 | .get_fc_host_stats = fc_get_host_stats, | ||
83 | .issue_fc_host_lip = fcoe_reset, | ||
84 | |||
85 | .terminate_rport_io = fc_rport_terminate_io, | ||
86 | }; | ||
87 | |||
88 | static struct scsi_host_template fcoe_sw_shost_template = { | ||
89 | .module = THIS_MODULE, | ||
90 | .name = "FCoE Driver", | ||
91 | .proc_name = FCOE_SW_NAME, | ||
92 | .queuecommand = fc_queuecommand, | ||
93 | .eh_abort_handler = fc_eh_abort, | ||
94 | .eh_device_reset_handler = fc_eh_device_reset, | ||
95 | .eh_host_reset_handler = fc_eh_host_reset, | ||
96 | .slave_alloc = fc_slave_alloc, | ||
97 | .change_queue_depth = fc_change_queue_depth, | ||
98 | .change_queue_type = fc_change_queue_type, | ||
99 | .this_id = -1, | ||
100 | .cmd_per_lun = 32, | ||
101 | .can_queue = FCOE_MAX_OUTSTANDING_COMMANDS, | ||
102 | .use_clustering = ENABLE_CLUSTERING, | ||
103 | .sg_tablesize = SG_ALL, | ||
104 | .max_sectors = 0xffff, | ||
105 | }; | ||
106 | |||
107 | /* | ||
108 | * fcoe_sw_lport_config - sets up the fc_lport | ||
109 | * @lp: ptr to the fc_lport | ||
110 | * @shost: ptr to the parent scsi host | ||
111 | * | ||
112 | * Returns: 0 for success | ||
113 | * | ||
114 | */ | ||
115 | static int fcoe_sw_lport_config(struct fc_lport *lp) | ||
116 | { | ||
117 | int i = 0; | ||
118 | |||
119 | lp->link_status = 0; | ||
120 | lp->max_retry_count = 3; | ||
121 | lp->e_d_tov = 2 * 1000; /* FC-FS default */ | ||
122 | lp->r_a_tov = 2 * 2 * 1000; | ||
123 | lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS | | ||
124 | FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL); | ||
125 | |||
126 | /* | ||
127 | * allocate per cpu stats block | ||
128 | */ | ||
129 | for_each_online_cpu(i) | ||
130 | lp->dev_stats[i] = kzalloc(sizeof(struct fcoe_dev_stats), | ||
131 | GFP_KERNEL); | ||
132 | |||
133 | /* lport fc_lport related configuration */ | ||
134 | fc_lport_config(lp); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * fcoe_sw_netdev_config - sets up fcoe_softc for lport and network | ||
141 | * related properties | ||
142 | * @lp : ptr to the fc_lport | ||
143 | * @netdev : ptr to the associated netdevice struct | ||
144 | * | ||
145 | * Must be called after fcoe_sw_lport_config() as it will use lport mutex | ||
146 | * | ||
147 | * Returns : 0 for success | ||
148 | * | ||
149 | */ | ||
150 | static int fcoe_sw_netdev_config(struct fc_lport *lp, struct net_device *netdev) | ||
151 | { | ||
152 | u32 mfs; | ||
153 | u64 wwnn, wwpn; | ||
154 | struct fcoe_softc *fc; | ||
155 | u8 flogi_maddr[ETH_ALEN]; | ||
156 | |||
157 | /* Setup lport private data to point to fcoe softc */ | ||
158 | fc = lport_priv(lp); | ||
159 | fc->lp = lp; | ||
160 | fc->real_dev = netdev; | ||
161 | fc->phys_dev = netdev; | ||
162 | |||
163 | /* Require support for get_pauseparam ethtool op. */ | ||
164 | if (netdev->priv_flags & IFF_802_1Q_VLAN) | ||
165 | fc->phys_dev = vlan_dev_real_dev(netdev); | ||
166 | |||
167 | /* Do not support for bonding device */ | ||
168 | if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) || | ||
169 | (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) || | ||
170 | (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) { | ||
171 | return -EOPNOTSUPP; | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * Determine max frame size based on underlying device and optional | ||
176 | * user-configured limit. If the MFS is too low, fcoe_link_ok() | ||
177 | * will return 0, so do this first. | ||
178 | */ | ||
179 | mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) + | ||
180 | sizeof(struct fcoe_crc_eof)); | ||
181 | if (fc_set_mfs(lp, mfs)) | ||
182 | return -EINVAL; | ||
183 | |||
184 | lp->link_status = ~FC_PAUSE & ~FC_LINK_UP; | ||
185 | if (!fcoe_link_ok(lp)) | ||
186 | lp->link_status |= FC_LINK_UP; | ||
187 | |||
188 | /* offload features support */ | ||
189 | if (fc->real_dev->features & NETIF_F_SG) | ||
190 | lp->sg_supp = 1; | ||
191 | |||
192 | |||
193 | skb_queue_head_init(&fc->fcoe_pending_queue); | ||
194 | |||
195 | /* setup Source Mac Address */ | ||
196 | memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr, | ||
197 | fc->real_dev->addr_len); | ||
198 | |||
199 | wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0); | ||
200 | fc_set_wwnn(lp, wwnn); | ||
201 | /* XXX - 3rd arg needs to be vlan id */ | ||
202 | wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0); | ||
203 | fc_set_wwpn(lp, wwpn); | ||
204 | |||
205 | /* | ||
206 | * Add FCoE MAC address as second unicast MAC address | ||
207 | * or enter promiscuous mode if not capable of listening | ||
208 | * for multiple unicast MACs. | ||
209 | */ | ||
210 | rtnl_lock(); | ||
211 | memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); | ||
212 | dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN); | ||
213 | rtnl_unlock(); | ||
214 | |||
215 | /* | ||
216 | * setup the receive function from ethernet driver | ||
217 | * on the ethertype for the given device | ||
218 | */ | ||
219 | fc->fcoe_packet_type.func = fcoe_rcv; | ||
220 | fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); | ||
221 | fc->fcoe_packet_type.dev = fc->real_dev; | ||
222 | dev_add_pack(&fc->fcoe_packet_type); | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | /* | ||
228 | * fcoe_sw_shost_config - sets up fc_lport->host | ||
229 | * @lp : ptr to the fc_lport | ||
230 | * @shost : ptr to the associated scsi host | ||
231 | * @dev : device associated to scsi host | ||
232 | * | ||
233 | * Must be called after fcoe_sw_lport_config) and fcoe_sw_netdev_config() | ||
234 | * | ||
235 | * Returns : 0 for success | ||
236 | * | ||
237 | */ | ||
238 | static int fcoe_sw_shost_config(struct fc_lport *lp, struct Scsi_Host *shost, | ||
239 | struct device *dev) | ||
240 | { | ||
241 | int rc = 0; | ||
242 | |||
243 | /* lport scsi host config */ | ||
244 | lp->host = shost; | ||
245 | |||
246 | lp->host->max_lun = FCOE_MAX_LUN; | ||
247 | lp->host->max_id = FCOE_MAX_FCP_TARGET; | ||
248 | lp->host->max_channel = 0; | ||
249 | lp->host->transportt = scsi_transport_fcoe_sw; | ||
250 | |||
251 | /* add the new host to the SCSI-ml */ | ||
252 | rc = scsi_add_host(lp->host, dev); | ||
253 | if (rc) { | ||
254 | FC_DBG("fcoe_sw_shost_config:error on scsi_add_host\n"); | ||
255 | return rc; | ||
256 | } | ||
257 | sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s", | ||
258 | FCOE_SW_NAME, FCOE_SW_VERSION, | ||
259 | fcoe_netdev(lp)->name); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * fcoe_sw_em_config - allocates em for this lport | ||
266 | * @lp: the port that em is to allocated for | ||
267 | * | ||
268 | * Returns : 0 on success | ||
269 | */ | ||
270 | static inline int fcoe_sw_em_config(struct fc_lport *lp) | ||
271 | { | ||
272 | BUG_ON(lp->emp); | ||
273 | |||
274 | lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3, | ||
275 | FCOE_MIN_XID, FCOE_MAX_XID); | ||
276 | if (!lp->emp) | ||
277 | return -ENOMEM; | ||
278 | |||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * fcoe_sw_destroy - FCoE software HBA tear-down function | ||
284 | * @netdev: ptr to the associated net_device | ||
285 | * | ||
286 | * Returns: 0 if link is OK for use by FCoE. | ||
287 | */ | ||
288 | static int fcoe_sw_destroy(struct net_device *netdev) | ||
289 | { | ||
290 | int cpu; | ||
291 | struct fc_lport *lp = NULL; | ||
292 | struct fcoe_softc *fc; | ||
293 | u8 flogi_maddr[ETH_ALEN]; | ||
294 | |||
295 | BUG_ON(!netdev); | ||
296 | |||
297 | printk(KERN_DEBUG "fcoe_sw_destroy:interface on %s\n", | ||
298 | netdev->name); | ||
299 | |||
300 | lp = fcoe_hostlist_lookup(netdev); | ||
301 | if (!lp) | ||
302 | return -ENODEV; | ||
303 | |||
304 | fc = fcoe_softc(lp); | ||
305 | |||
306 | /* Logout of the fabric */ | ||
307 | fc_fabric_logoff(lp); | ||
308 | |||
309 | /* Remove the instance from fcoe's list */ | ||
310 | fcoe_hostlist_remove(lp); | ||
311 | |||
312 | /* Don't listen for Ethernet packets anymore */ | ||
313 | dev_remove_pack(&fc->fcoe_packet_type); | ||
314 | |||
315 | /* Cleanup the fc_lport */ | ||
316 | fc_lport_destroy(lp); | ||
317 | fc_fcp_destroy(lp); | ||
318 | |||
319 | /* Detach from the scsi-ml */ | ||
320 | fc_remove_host(lp->host); | ||
321 | scsi_remove_host(lp->host); | ||
322 | |||
323 | /* There are no more rports or I/O, free the EM */ | ||
324 | if (lp->emp) | ||
325 | fc_exch_mgr_free(lp->emp); | ||
326 | |||
327 | /* Delete secondary MAC addresses */ | ||
328 | rtnl_lock(); | ||
329 | memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); | ||
330 | dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN); | ||
331 | if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 })) | ||
332 | dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN); | ||
333 | rtnl_unlock(); | ||
334 | |||
335 | /* Free the per-CPU revieve threads */ | ||
336 | fcoe_percpu_clean(lp); | ||
337 | |||
338 | /* Free existing skbs */ | ||
339 | fcoe_clean_pending_queue(lp); | ||
340 | |||
341 | /* Free memory used by statistical counters */ | ||
342 | for_each_online_cpu(cpu) | ||
343 | kfree(lp->dev_stats[cpu]); | ||
344 | |||
345 | /* Release the net_device and Scsi_Host */ | ||
346 | dev_put(fc->real_dev); | ||
347 | scsi_host_put(lp->host); | ||
348 | |||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | static struct libfc_function_template fcoe_sw_libfc_fcn_templ = { | ||
353 | .frame_send = fcoe_xmit, | ||
354 | }; | ||
355 | |||
356 | /* | ||
357 | * fcoe_sw_create - this function creates the fcoe interface | ||
358 | * @netdev: pointer the associated netdevice | ||
359 | * | ||
360 | * Creates fc_lport struct and scsi_host for lport, configures lport | ||
361 | * and starts fabric login. | ||
362 | * | ||
363 | * Returns : 0 on success | ||
364 | */ | ||
365 | static int fcoe_sw_create(struct net_device *netdev) | ||
366 | { | ||
367 | int rc; | ||
368 | struct fc_lport *lp = NULL; | ||
369 | struct fcoe_softc *fc; | ||
370 | struct Scsi_Host *shost; | ||
371 | |||
372 | BUG_ON(!netdev); | ||
373 | |||
374 | printk(KERN_DEBUG "fcoe_sw_create:interface on %s\n", | ||
375 | netdev->name); | ||
376 | |||
377 | lp = fcoe_hostlist_lookup(netdev); | ||
378 | if (lp) | ||
379 | return -EEXIST; | ||
380 | |||
381 | shost = fcoe_host_alloc(&fcoe_sw_shost_template, | ||
382 | sizeof(struct fcoe_softc)); | ||
383 | if (!shost) { | ||
384 | FC_DBG("Could not allocate host structure\n"); | ||
385 | return -ENOMEM; | ||
386 | } | ||
387 | lp = shost_priv(shost); | ||
388 | fc = lport_priv(lp); | ||
389 | |||
390 | /* configure fc_lport, e.g., em */ | ||
391 | rc = fcoe_sw_lport_config(lp); | ||
392 | if (rc) { | ||
393 | FC_DBG("Could not configure lport\n"); | ||
394 | goto out_host_put; | ||
395 | } | ||
396 | |||
397 | /* configure lport network properties */ | ||
398 | rc = fcoe_sw_netdev_config(lp, netdev); | ||
399 | if (rc) { | ||
400 | FC_DBG("Could not configure netdev for lport\n"); | ||
401 | goto out_host_put; | ||
402 | } | ||
403 | |||
404 | /* configure lport scsi host properties */ | ||
405 | rc = fcoe_sw_shost_config(lp, shost, &netdev->dev); | ||
406 | if (rc) { | ||
407 | FC_DBG("Could not configure shost for lport\n"); | ||
408 | goto out_host_put; | ||
409 | } | ||
410 | |||
411 | /* lport exch manager allocation */ | ||
412 | rc = fcoe_sw_em_config(lp); | ||
413 | if (rc) { | ||
414 | FC_DBG("Could not configure em for lport\n"); | ||
415 | goto out_host_put; | ||
416 | } | ||
417 | |||
418 | /* Initialize the library */ | ||
419 | rc = fcoe_libfc_config(lp, &fcoe_sw_libfc_fcn_templ); | ||
420 | if (rc) { | ||
421 | FC_DBG("Could not configure libfc for lport!\n"); | ||
422 | goto out_lp_destroy; | ||
423 | } | ||
424 | |||
425 | /* add to lports list */ | ||
426 | fcoe_hostlist_add(lp); | ||
427 | |||
428 | lp->boot_time = jiffies; | ||
429 | |||
430 | fc_fabric_login(lp); | ||
431 | |||
432 | dev_hold(netdev); | ||
433 | |||
434 | return rc; | ||
435 | |||
436 | out_lp_destroy: | ||
437 | fc_exch_mgr_free(lp->emp); /* Free the EM */ | ||
438 | out_host_put: | ||
439 | scsi_host_put(lp->host); | ||
440 | return rc; | ||
441 | } | ||
442 | |||
443 | /* | ||
444 | * fcoe_sw_match - the fcoe sw transport match function | ||
445 | * | ||
446 | * Returns : false always | ||
447 | */ | ||
448 | static bool fcoe_sw_match(struct net_device *netdev) | ||
449 | { | ||
450 | /* FIXME - for sw transport, always return false */ | ||
451 | return false; | ||
452 | } | ||
453 | |||
454 | /* the sw hba fcoe transport */ | ||
455 | struct fcoe_transport fcoe_sw_transport = { | ||
456 | .name = "fcoesw", | ||
457 | .create = fcoe_sw_create, | ||
458 | .destroy = fcoe_sw_destroy, | ||
459 | .match = fcoe_sw_match, | ||
460 | .vendor = 0x0, | ||
461 | .device = 0xffff, | ||
462 | }; | ||
463 | |||
464 | /* | ||
465 | * fcoe_sw_init - registers fcoe_sw_transport | ||
466 | * | ||
467 | * Returns : 0 on success | ||
468 | */ | ||
469 | int __init fcoe_sw_init(void) | ||
470 | { | ||
471 | /* attach to scsi transport */ | ||
472 | scsi_transport_fcoe_sw = | ||
473 | fc_attach_transport(&fcoe_sw_transport_function); | ||
474 | if (!scsi_transport_fcoe_sw) { | ||
475 | printk(KERN_ERR "fcoe_sw_init:fc_attach_transport() failed\n"); | ||
476 | return -ENODEV; | ||
477 | } | ||
478 | /* register sw transport */ | ||
479 | fcoe_transport_register(&fcoe_sw_transport); | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | /* | ||
484 | * fcoe_sw_exit - unregisters fcoe_sw_transport | ||
485 | * | ||
486 | * Returns : 0 on success | ||
487 | */ | ||
488 | int __exit fcoe_sw_exit(void) | ||
489 | { | ||
490 | /* dettach the transport */ | ||
491 | fc_release_transport(scsi_transport_fcoe_sw); | ||
492 | fcoe_transport_unregister(&fcoe_sw_transport); | ||
493 | return 0; | ||
494 | } | ||
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c new file mode 100644 index 000000000000..1cb549c4fac4 --- /dev/null +++ b/drivers/scsi/fcoe/libfcoe.c | |||
@@ -0,0 +1,1510 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2007 - 2008 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/version.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/skbuff.h> | ||
25 | #include <linux/netdevice.h> | ||
26 | #include <linux/etherdevice.h> | ||
27 | #include <linux/ethtool.h> | ||
28 | #include <linux/if_ether.h> | ||
29 | #include <linux/if_vlan.h> | ||
30 | #include <linux/kthread.h> | ||
31 | #include <linux/crc32.h> | ||
32 | #include <linux/cpu.h> | ||
33 | #include <linux/fs.h> | ||
34 | #include <linux/sysfs.h> | ||
35 | #include <linux/ctype.h> | ||
36 | #include <scsi/scsi_tcq.h> | ||
37 | #include <scsi/scsicam.h> | ||
38 | #include <scsi/scsi_transport.h> | ||
39 | #include <scsi/scsi_transport_fc.h> | ||
40 | #include <net/rtnetlink.h> | ||
41 | |||
42 | #include <scsi/fc/fc_encaps.h> | ||
43 | |||
44 | #include <scsi/libfc.h> | ||
45 | #include <scsi/fc_frame.h> | ||
46 | #include <scsi/libfcoe.h> | ||
47 | #include <scsi/fc_transport_fcoe.h> | ||
48 | |||
49 | static int debug_fcoe; | ||
50 | |||
51 | #define FCOE_MAX_QUEUE_DEPTH 256 | ||
52 | |||
53 | /* destination address mode */ | ||
54 | #define FCOE_GW_ADDR_MODE 0x00 | ||
55 | #define FCOE_FCOUI_ADDR_MODE 0x01 | ||
56 | |||
57 | #define FCOE_WORD_TO_BYTE 4 | ||
58 | |||
59 | MODULE_AUTHOR("Open-FCoE.org"); | ||
60 | MODULE_DESCRIPTION("FCoE"); | ||
61 | MODULE_LICENSE("GPL"); | ||
62 | |||
63 | /* fcoe host list */ | ||
64 | LIST_HEAD(fcoe_hostlist); | ||
65 | DEFINE_RWLOCK(fcoe_hostlist_lock); | ||
66 | DEFINE_TIMER(fcoe_timer, NULL, 0, 0); | ||
67 | struct fcoe_percpu_s *fcoe_percpu[NR_CPUS]; | ||
68 | |||
69 | |||
70 | /* Function Prototyes */ | ||
71 | static int fcoe_check_wait_queue(struct fc_lport *); | ||
72 | static void fcoe_insert_wait_queue_head(struct fc_lport *, struct sk_buff *); | ||
73 | static void fcoe_insert_wait_queue(struct fc_lport *, struct sk_buff *); | ||
74 | static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *); | ||
75 | #ifdef CONFIG_HOTPLUG_CPU | ||
76 | static int fcoe_cpu_callback(struct notifier_block *, ulong, void *); | ||
77 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
78 | static int fcoe_device_notification(struct notifier_block *, ulong, void *); | ||
79 | static void fcoe_dev_setup(void); | ||
80 | static void fcoe_dev_cleanup(void); | ||
81 | |||
82 | /* notification function from net device */ | ||
83 | static struct notifier_block fcoe_notifier = { | ||
84 | .notifier_call = fcoe_device_notification, | ||
85 | }; | ||
86 | |||
87 | |||
88 | #ifdef CONFIG_HOTPLUG_CPU | ||
89 | static struct notifier_block fcoe_cpu_notifier = { | ||
90 | .notifier_call = fcoe_cpu_callback, | ||
91 | }; | ||
92 | |||
93 | /** | ||
94 | * fcoe_create_percpu_data - creates the associated cpu data | ||
95 | * @cpu: index for the cpu where fcoe cpu data will be created | ||
96 | * | ||
97 | * create percpu stats block, from cpu add notifier | ||
98 | * | ||
99 | * Returns: none | ||
100 | **/ | ||
101 | static void fcoe_create_percpu_data(int cpu) | ||
102 | { | ||
103 | struct fc_lport *lp; | ||
104 | struct fcoe_softc *fc; | ||
105 | |||
106 | write_lock_bh(&fcoe_hostlist_lock); | ||
107 | list_for_each_entry(fc, &fcoe_hostlist, list) { | ||
108 | lp = fc->lp; | ||
109 | if (lp->dev_stats[cpu] == NULL) | ||
110 | lp->dev_stats[cpu] = | ||
111 | kzalloc(sizeof(struct fcoe_dev_stats), | ||
112 | GFP_KERNEL); | ||
113 | } | ||
114 | write_unlock_bh(&fcoe_hostlist_lock); | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * fcoe_destroy_percpu_data - destroys the associated cpu data | ||
119 | * @cpu: index for the cpu where fcoe cpu data will destroyed | ||
120 | * | ||
121 | * destroy percpu stats block called by cpu add/remove notifier | ||
122 | * | ||
123 | * Retuns: none | ||
124 | **/ | ||
125 | static void fcoe_destroy_percpu_data(int cpu) | ||
126 | { | ||
127 | struct fc_lport *lp; | ||
128 | struct fcoe_softc *fc; | ||
129 | |||
130 | write_lock_bh(&fcoe_hostlist_lock); | ||
131 | list_for_each_entry(fc, &fcoe_hostlist, list) { | ||
132 | lp = fc->lp; | ||
133 | kfree(lp->dev_stats[cpu]); | ||
134 | lp->dev_stats[cpu] = NULL; | ||
135 | } | ||
136 | write_unlock_bh(&fcoe_hostlist_lock); | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * fcoe_cpu_callback - fcoe cpu hotplug event callback | ||
141 | * @nfb: callback data block | ||
142 | * @action: event triggering the callback | ||
143 | * @hcpu: index for the cpu of this event | ||
144 | * | ||
145 | * this creates or destroys per cpu data for fcoe | ||
146 | * | ||
147 | * Returns NOTIFY_OK always. | ||
148 | **/ | ||
149 | static int fcoe_cpu_callback(struct notifier_block *nfb, unsigned long action, | ||
150 | void *hcpu) | ||
151 | { | ||
152 | unsigned int cpu = (unsigned long)hcpu; | ||
153 | |||
154 | switch (action) { | ||
155 | case CPU_ONLINE: | ||
156 | fcoe_create_percpu_data(cpu); | ||
157 | break; | ||
158 | case CPU_DEAD: | ||
159 | fcoe_destroy_percpu_data(cpu); | ||
160 | break; | ||
161 | default: | ||
162 | break; | ||
163 | } | ||
164 | return NOTIFY_OK; | ||
165 | } | ||
166 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
167 | |||
168 | /** | ||
169 | * foce_rcv - this is the fcoe receive function called by NET_RX_SOFTIRQ | ||
170 | * @skb: the receive skb | ||
171 | * @dev: associated net device | ||
172 | * @ptype: context | ||
173 | * @odldev: last device | ||
174 | * | ||
175 | * this function will receive the packet and build fc frame and pass it up | ||
176 | * | ||
177 | * Returns: 0 for success | ||
178 | **/ | ||
179 | int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, | ||
180 | struct packet_type *ptype, struct net_device *olddev) | ||
181 | { | ||
182 | struct fc_lport *lp; | ||
183 | struct fcoe_rcv_info *fr; | ||
184 | struct fcoe_softc *fc; | ||
185 | struct fcoe_dev_stats *stats; | ||
186 | struct fc_frame_header *fh; | ||
187 | unsigned short oxid; | ||
188 | int cpu_idx; | ||
189 | struct fcoe_percpu_s *fps; | ||
190 | |||
191 | fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type); | ||
192 | lp = fc->lp; | ||
193 | if (unlikely(lp == NULL)) { | ||
194 | FC_DBG("cannot find hba structure"); | ||
195 | goto err2; | ||
196 | } | ||
197 | |||
198 | if (unlikely(debug_fcoe)) { | ||
199 | FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p " | ||
200 | "end:%p sum:%d dev:%s", skb->len, skb->data_len, | ||
201 | skb->head, skb->data, skb_tail_pointer(skb), | ||
202 | skb_end_pointer(skb), skb->csum, | ||
203 | skb->dev ? skb->dev->name : "<NULL>"); | ||
204 | |||
205 | } | ||
206 | |||
207 | /* check for FCOE packet type */ | ||
208 | if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) { | ||
209 | FC_DBG("wrong FC type frame"); | ||
210 | goto err; | ||
211 | } | ||
212 | |||
213 | /* | ||
214 | * Check for minimum frame length, and make sure required FCoE | ||
215 | * and FC headers are pulled into the linear data area. | ||
216 | */ | ||
217 | if (unlikely((skb->len < FCOE_MIN_FRAME) || | ||
218 | !pskb_may_pull(skb, FCOE_HEADER_LEN))) | ||
219 | goto err; | ||
220 | |||
221 | skb_set_transport_header(skb, sizeof(struct fcoe_hdr)); | ||
222 | fh = (struct fc_frame_header *) skb_transport_header(skb); | ||
223 | |||
224 | oxid = ntohs(fh->fh_ox_id); | ||
225 | |||
226 | fr = fcoe_dev_from_skb(skb); | ||
227 | fr->fr_dev = lp; | ||
228 | fr->ptype = ptype; | ||
229 | cpu_idx = 0; | ||
230 | #ifdef CONFIG_SMP | ||
231 | /* | ||
232 | * The incoming frame exchange id(oxid) is ANDed with num of online | ||
233 | * cpu bits to get cpu_idx and then this cpu_idx is used for selecting | ||
234 | * a per cpu kernel thread from fcoe_percpu. In case the cpu is | ||
235 | * offline or no kernel thread for derived cpu_idx then cpu_idx is | ||
236 | * initialize to first online cpu index. | ||
237 | */ | ||
238 | cpu_idx = oxid & (num_online_cpus() - 1); | ||
239 | if (!fcoe_percpu[cpu_idx] || !cpu_online(cpu_idx)) | ||
240 | cpu_idx = first_cpu(cpu_online_map); | ||
241 | #endif | ||
242 | fps = fcoe_percpu[cpu_idx]; | ||
243 | |||
244 | spin_lock_bh(&fps->fcoe_rx_list.lock); | ||
245 | __skb_queue_tail(&fps->fcoe_rx_list, skb); | ||
246 | if (fps->fcoe_rx_list.qlen == 1) | ||
247 | wake_up_process(fps->thread); | ||
248 | |||
249 | spin_unlock_bh(&fps->fcoe_rx_list.lock); | ||
250 | |||
251 | return 0; | ||
252 | err: | ||
253 | #ifdef CONFIG_SMP | ||
254 | stats = lp->dev_stats[smp_processor_id()]; | ||
255 | #else | ||
256 | stats = lp->dev_stats[0]; | ||
257 | #endif | ||
258 | if (stats) | ||
259 | stats->ErrorFrames++; | ||
260 | |||
261 | err2: | ||
262 | kfree_skb(skb); | ||
263 | return -1; | ||
264 | } | ||
265 | EXPORT_SYMBOL_GPL(fcoe_rcv); | ||
266 | |||
267 | /** | ||
268 | * fcoe_start_io - pass to netdev to start xmit for fcoe | ||
269 | * @skb: the skb to be xmitted | ||
270 | * | ||
271 | * Returns: 0 for success | ||
272 | **/ | ||
273 | static inline int fcoe_start_io(struct sk_buff *skb) | ||
274 | { | ||
275 | int rc; | ||
276 | |||
277 | skb_get(skb); | ||
278 | rc = dev_queue_xmit(skb); | ||
279 | if (rc != 0) | ||
280 | return rc; | ||
281 | kfree_skb(skb); | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | /** | ||
286 | * fcoe_get_paged_crc_eof - in case we need alloc a page for crc_eof | ||
287 | * @skb: the skb to be xmitted | ||
288 | * @tlen: total len | ||
289 | * | ||
290 | * Returns: 0 for success | ||
291 | **/ | ||
292 | static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen) | ||
293 | { | ||
294 | struct fcoe_percpu_s *fps; | ||
295 | struct page *page; | ||
296 | int cpu_idx; | ||
297 | |||
298 | cpu_idx = get_cpu(); | ||
299 | fps = fcoe_percpu[cpu_idx]; | ||
300 | page = fps->crc_eof_page; | ||
301 | if (!page) { | ||
302 | page = alloc_page(GFP_ATOMIC); | ||
303 | if (!page) { | ||
304 | put_cpu(); | ||
305 | return -ENOMEM; | ||
306 | } | ||
307 | fps->crc_eof_page = page; | ||
308 | WARN_ON(fps->crc_eof_offset != 0); | ||
309 | } | ||
310 | |||
311 | get_page(page); | ||
312 | skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, | ||
313 | fps->crc_eof_offset, tlen); | ||
314 | skb->len += tlen; | ||
315 | skb->data_len += tlen; | ||
316 | skb->truesize += tlen; | ||
317 | fps->crc_eof_offset += sizeof(struct fcoe_crc_eof); | ||
318 | |||
319 | if (fps->crc_eof_offset >= PAGE_SIZE) { | ||
320 | fps->crc_eof_page = NULL; | ||
321 | fps->crc_eof_offset = 0; | ||
322 | put_page(page); | ||
323 | } | ||
324 | put_cpu(); | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | /** | ||
329 | * fcoe_fc_crc - calculates FC CRC in this fcoe skb | ||
330 | * @fp: the fc_frame containg data to be checksummed | ||
331 | * | ||
332 | * This uses crc32() to calculate the crc for fc frame | ||
333 | * Return : 32 bit crc | ||
334 | * | ||
335 | **/ | ||
336 | u32 fcoe_fc_crc(struct fc_frame *fp) | ||
337 | { | ||
338 | struct sk_buff *skb = fp_skb(fp); | ||
339 | struct skb_frag_struct *frag; | ||
340 | unsigned char *data; | ||
341 | unsigned long off, len, clen; | ||
342 | u32 crc; | ||
343 | unsigned i; | ||
344 | |||
345 | crc = crc32(~0, skb->data, skb_headlen(skb)); | ||
346 | |||
347 | for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { | ||
348 | frag = &skb_shinfo(skb)->frags[i]; | ||
349 | off = frag->page_offset; | ||
350 | len = frag->size; | ||
351 | while (len > 0) { | ||
352 | clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK)); | ||
353 | data = kmap_atomic(frag->page + (off >> PAGE_SHIFT), | ||
354 | KM_SKB_DATA_SOFTIRQ); | ||
355 | crc = crc32(crc, data + (off & ~PAGE_MASK), clen); | ||
356 | kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ); | ||
357 | off += clen; | ||
358 | len -= clen; | ||
359 | } | ||
360 | } | ||
361 | return crc; | ||
362 | } | ||
363 | EXPORT_SYMBOL_GPL(fcoe_fc_crc); | ||
364 | |||
365 | /** | ||
366 | * fcoe_xmit - FCoE frame transmit function | ||
367 | * @lp: the associated local port | ||
368 | * @fp: the fc_frame to be transmitted | ||
369 | * | ||
370 | * Return : 0 for success | ||
371 | * | ||
372 | **/ | ||
373 | int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) | ||
374 | { | ||
375 | int wlen, rc = 0; | ||
376 | u32 crc; | ||
377 | struct ethhdr *eh; | ||
378 | struct fcoe_crc_eof *cp; | ||
379 | struct sk_buff *skb; | ||
380 | struct fcoe_dev_stats *stats; | ||
381 | struct fc_frame_header *fh; | ||
382 | unsigned int hlen; /* header length implies the version */ | ||
383 | unsigned int tlen; /* trailer length */ | ||
384 | unsigned int elen; /* eth header, may include vlan */ | ||
385 | int flogi_in_progress = 0; | ||
386 | struct fcoe_softc *fc; | ||
387 | u8 sof, eof; | ||
388 | struct fcoe_hdr *hp; | ||
389 | |||
390 | WARN_ON((fr_len(fp) % sizeof(u32)) != 0); | ||
391 | |||
392 | fc = fcoe_softc(lp); | ||
393 | /* | ||
394 | * if it is a flogi then we need to learn gw-addr | ||
395 | * and my own fcid | ||
396 | */ | ||
397 | fh = fc_frame_header_get(fp); | ||
398 | if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) { | ||
399 | if (fc_frame_payload_op(fp) == ELS_FLOGI) { | ||
400 | fc->flogi_oxid = ntohs(fh->fh_ox_id); | ||
401 | fc->address_mode = FCOE_FCOUI_ADDR_MODE; | ||
402 | fc->flogi_progress = 1; | ||
403 | flogi_in_progress = 1; | ||
404 | } else if (fc->flogi_progress && ntoh24(fh->fh_s_id) != 0) { | ||
405 | /* | ||
406 | * Here we must've gotten an SID by accepting an FLOGI | ||
407 | * from a point-to-point connection. Switch to using | ||
408 | * the source mac based on the SID. The destination | ||
409 | * MAC in this case would have been set by receving the | ||
410 | * FLOGI. | ||
411 | */ | ||
412 | fc_fcoe_set_mac(fc->data_src_addr, fh->fh_s_id); | ||
413 | fc->flogi_progress = 0; | ||
414 | } | ||
415 | } | ||
416 | |||
417 | skb = fp_skb(fp); | ||
418 | sof = fr_sof(fp); | ||
419 | eof = fr_eof(fp); | ||
420 | |||
421 | elen = (fc->real_dev->priv_flags & IFF_802_1Q_VLAN) ? | ||
422 | sizeof(struct vlan_ethhdr) : sizeof(struct ethhdr); | ||
423 | hlen = sizeof(struct fcoe_hdr); | ||
424 | tlen = sizeof(struct fcoe_crc_eof); | ||
425 | wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE; | ||
426 | |||
427 | /* crc offload */ | ||
428 | if (likely(lp->crc_offload)) { | ||
429 | skb->ip_summed = CHECKSUM_COMPLETE; | ||
430 | skb->csum_start = skb_headroom(skb); | ||
431 | skb->csum_offset = skb->len; | ||
432 | crc = 0; | ||
433 | } else { | ||
434 | skb->ip_summed = CHECKSUM_NONE; | ||
435 | crc = fcoe_fc_crc(fp); | ||
436 | } | ||
437 | |||
438 | /* copy fc crc and eof to the skb buff */ | ||
439 | if (skb_is_nonlinear(skb)) { | ||
440 | skb_frag_t *frag; | ||
441 | if (fcoe_get_paged_crc_eof(skb, tlen)) { | ||
442 | kfree(skb); | ||
443 | return -ENOMEM; | ||
444 | } | ||
445 | frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1]; | ||
446 | cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ) | ||
447 | + frag->page_offset; | ||
448 | } else { | ||
449 | cp = (struct fcoe_crc_eof *)skb_put(skb, tlen); | ||
450 | } | ||
451 | |||
452 | memset(cp, 0, sizeof(*cp)); | ||
453 | cp->fcoe_eof = eof; | ||
454 | cp->fcoe_crc32 = cpu_to_le32(~crc); | ||
455 | |||
456 | if (skb_is_nonlinear(skb)) { | ||
457 | kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ); | ||
458 | cp = NULL; | ||
459 | } | ||
460 | |||
461 | /* adjust skb netowrk/transport offsets to match mac/fcoe/fc */ | ||
462 | skb_push(skb, elen + hlen); | ||
463 | skb_reset_mac_header(skb); | ||
464 | skb_reset_network_header(skb); | ||
465 | skb->mac_len = elen; | ||
466 | skb->protocol = htons(ETH_P_802_3); | ||
467 | skb->dev = fc->real_dev; | ||
468 | |||
469 | /* fill up mac and fcoe headers */ | ||
470 | eh = eth_hdr(skb); | ||
471 | eh->h_proto = htons(ETH_P_FCOE); | ||
472 | if (fc->address_mode == FCOE_FCOUI_ADDR_MODE) | ||
473 | fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id); | ||
474 | else | ||
475 | /* insert GW address */ | ||
476 | memcpy(eh->h_dest, fc->dest_addr, ETH_ALEN); | ||
477 | |||
478 | if (unlikely(flogi_in_progress)) | ||
479 | memcpy(eh->h_source, fc->ctl_src_addr, ETH_ALEN); | ||
480 | else | ||
481 | memcpy(eh->h_source, fc->data_src_addr, ETH_ALEN); | ||
482 | |||
483 | hp = (struct fcoe_hdr *)(eh + 1); | ||
484 | memset(hp, 0, sizeof(*hp)); | ||
485 | if (FC_FCOE_VER) | ||
486 | FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER); | ||
487 | hp->fcoe_sof = sof; | ||
488 | |||
489 | /* update tx stats: regardless if LLD fails */ | ||
490 | stats = lp->dev_stats[smp_processor_id()]; | ||
491 | if (stats) { | ||
492 | stats->TxFrames++; | ||
493 | stats->TxWords += wlen; | ||
494 | } | ||
495 | |||
496 | /* send down to lld */ | ||
497 | fr_dev(fp) = lp; | ||
498 | if (fc->fcoe_pending_queue.qlen) | ||
499 | rc = fcoe_check_wait_queue(lp); | ||
500 | |||
501 | if (rc == 0) | ||
502 | rc = fcoe_start_io(skb); | ||
503 | |||
504 | if (rc) { | ||
505 | fcoe_insert_wait_queue(lp, skb); | ||
506 | if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) | ||
507 | fc_pause(lp); | ||
508 | } | ||
509 | |||
510 | return 0; | ||
511 | } | ||
512 | EXPORT_SYMBOL_GPL(fcoe_xmit); | ||
513 | |||
514 | /* | ||
515 | * fcoe_percpu_receive_thread - recv thread per cpu | ||
516 | * @arg: ptr to the fcoe per cpu struct | ||
517 | * | ||
518 | * Return: 0 for success | ||
519 | * | ||
520 | */ | ||
521 | int fcoe_percpu_receive_thread(void *arg) | ||
522 | { | ||
523 | struct fcoe_percpu_s *p = arg; | ||
524 | u32 fr_len; | ||
525 | struct fc_lport *lp; | ||
526 | struct fcoe_rcv_info *fr; | ||
527 | struct fcoe_dev_stats *stats; | ||
528 | struct fc_frame_header *fh; | ||
529 | struct sk_buff *skb; | ||
530 | struct fcoe_crc_eof crc_eof; | ||
531 | struct fc_frame *fp; | ||
532 | u8 *mac = NULL; | ||
533 | struct fcoe_softc *fc; | ||
534 | struct fcoe_hdr *hp; | ||
535 | |||
536 | set_user_nice(current, 19); | ||
537 | |||
538 | while (!kthread_should_stop()) { | ||
539 | |||
540 | spin_lock_bh(&p->fcoe_rx_list.lock); | ||
541 | while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) { | ||
542 | set_current_state(TASK_INTERRUPTIBLE); | ||
543 | spin_unlock_bh(&p->fcoe_rx_list.lock); | ||
544 | schedule(); | ||
545 | set_current_state(TASK_RUNNING); | ||
546 | if (kthread_should_stop()) | ||
547 | return 0; | ||
548 | spin_lock_bh(&p->fcoe_rx_list.lock); | ||
549 | } | ||
550 | spin_unlock_bh(&p->fcoe_rx_list.lock); | ||
551 | fr = fcoe_dev_from_skb(skb); | ||
552 | lp = fr->fr_dev; | ||
553 | if (unlikely(lp == NULL)) { | ||
554 | FC_DBG("invalid HBA Structure"); | ||
555 | kfree_skb(skb); | ||
556 | continue; | ||
557 | } | ||
558 | |||
559 | stats = lp->dev_stats[smp_processor_id()]; | ||
560 | |||
561 | if (unlikely(debug_fcoe)) { | ||
562 | FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p " | ||
563 | "tail:%p end:%p sum:%d dev:%s", | ||
564 | skb->len, skb->data_len, | ||
565 | skb->head, skb->data, skb_tail_pointer(skb), | ||
566 | skb_end_pointer(skb), skb->csum, | ||
567 | skb->dev ? skb->dev->name : "<NULL>"); | ||
568 | } | ||
569 | |||
570 | /* | ||
571 | * Save source MAC address before discarding header. | ||
572 | */ | ||
573 | fc = lport_priv(lp); | ||
574 | if (unlikely(fc->flogi_progress)) | ||
575 | mac = eth_hdr(skb)->h_source; | ||
576 | |||
577 | if (skb_is_nonlinear(skb)) | ||
578 | skb_linearize(skb); /* not ideal */ | ||
579 | |||
580 | /* | ||
581 | * Frame length checks and setting up the header pointers | ||
582 | * was done in fcoe_rcv already. | ||
583 | */ | ||
584 | hp = (struct fcoe_hdr *) skb_network_header(skb); | ||
585 | fh = (struct fc_frame_header *) skb_transport_header(skb); | ||
586 | |||
587 | if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { | ||
588 | if (stats) { | ||
589 | if (stats->ErrorFrames < 5) | ||
590 | FC_DBG("unknown FCoE version %x", | ||
591 | FC_FCOE_DECAPS_VER(hp)); | ||
592 | stats->ErrorFrames++; | ||
593 | } | ||
594 | kfree_skb(skb); | ||
595 | continue; | ||
596 | } | ||
597 | |||
598 | skb_pull(skb, sizeof(struct fcoe_hdr)); | ||
599 | fr_len = skb->len - sizeof(struct fcoe_crc_eof); | ||
600 | |||
601 | if (stats) { | ||
602 | stats->RxFrames++; | ||
603 | stats->RxWords += fr_len / FCOE_WORD_TO_BYTE; | ||
604 | } | ||
605 | |||
606 | fp = (struct fc_frame *)skb; | ||
607 | fc_frame_init(fp); | ||
608 | fr_dev(fp) = lp; | ||
609 | fr_sof(fp) = hp->fcoe_sof; | ||
610 | |||
611 | /* Copy out the CRC and EOF trailer for access */ | ||
612 | if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) { | ||
613 | kfree_skb(skb); | ||
614 | continue; | ||
615 | } | ||
616 | fr_eof(fp) = crc_eof.fcoe_eof; | ||
617 | fr_crc(fp) = crc_eof.fcoe_crc32; | ||
618 | if (pskb_trim(skb, fr_len)) { | ||
619 | kfree_skb(skb); | ||
620 | continue; | ||
621 | } | ||
622 | |||
623 | /* | ||
624 | * We only check CRC if no offload is available and if it is | ||
625 | * it's solicited data, in which case, the FCP layer would | ||
626 | * check it during the copy. | ||
627 | */ | ||
628 | if (lp->crc_offload) | ||
629 | fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; | ||
630 | else | ||
631 | fr_flags(fp) |= FCPHF_CRC_UNCHECKED; | ||
632 | |||
633 | fh = fc_frame_header_get(fp); | ||
634 | if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && | ||
635 | fh->fh_type == FC_TYPE_FCP) { | ||
636 | fc_exch_recv(lp, lp->emp, fp); | ||
637 | continue; | ||
638 | } | ||
639 | if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { | ||
640 | if (le32_to_cpu(fr_crc(fp)) != | ||
641 | ~crc32(~0, skb->data, fr_len)) { | ||
642 | if (debug_fcoe || stats->InvalidCRCCount < 5) | ||
643 | printk(KERN_WARNING "fcoe: dropping " | ||
644 | "frame with CRC error\n"); | ||
645 | stats->InvalidCRCCount++; | ||
646 | stats->ErrorFrames++; | ||
647 | fc_frame_free(fp); | ||
648 | continue; | ||
649 | } | ||
650 | fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; | ||
651 | } | ||
652 | /* non flogi and non data exchanges are handled here */ | ||
653 | if (unlikely(fc->flogi_progress)) | ||
654 | fcoe_recv_flogi(fc, fp, mac); | ||
655 | fc_exch_recv(lp, lp->emp, fp); | ||
656 | } | ||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | /** | ||
661 | * fcoe_recv_flogi - flogi receive function | ||
662 | * @fc: associated fcoe_softc | ||
663 | * @fp: the recieved frame | ||
664 | * @sa: the source address of this flogi | ||
665 | * | ||
666 | * This is responsible to parse the flogi response and sets the corresponding | ||
667 | * mac address for the initiator, eitehr OUI based or GW based. | ||
668 | * | ||
669 | * Returns: none | ||
670 | **/ | ||
671 | static void fcoe_recv_flogi(struct fcoe_softc *fc, struct fc_frame *fp, u8 *sa) | ||
672 | { | ||
673 | struct fc_frame_header *fh; | ||
674 | u8 op; | ||
675 | |||
676 | fh = fc_frame_header_get(fp); | ||
677 | if (fh->fh_type != FC_TYPE_ELS) | ||
678 | return; | ||
679 | op = fc_frame_payload_op(fp); | ||
680 | if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP && | ||
681 | fc->flogi_oxid == ntohs(fh->fh_ox_id)) { | ||
682 | /* | ||
683 | * FLOGI accepted. | ||
684 | * If the src mac addr is FC_OUI-based, then we mark the | ||
685 | * address_mode flag to use FC_OUI-based Ethernet DA. | ||
686 | * Otherwise we use the FCoE gateway addr | ||
687 | */ | ||
688 | if (!compare_ether_addr(sa, (u8[6]) FC_FCOE_FLOGI_MAC)) { | ||
689 | fc->address_mode = FCOE_FCOUI_ADDR_MODE; | ||
690 | } else { | ||
691 | memcpy(fc->dest_addr, sa, ETH_ALEN); | ||
692 | fc->address_mode = FCOE_GW_ADDR_MODE; | ||
693 | } | ||
694 | |||
695 | /* | ||
696 | * Remove any previously-set unicast MAC filter. | ||
697 | * Add secondary FCoE MAC address filter for our OUI. | ||
698 | */ | ||
699 | rtnl_lock(); | ||
700 | if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 })) | ||
701 | dev_unicast_delete(fc->real_dev, fc->data_src_addr, | ||
702 | ETH_ALEN); | ||
703 | fc_fcoe_set_mac(fc->data_src_addr, fh->fh_d_id); | ||
704 | dev_unicast_add(fc->real_dev, fc->data_src_addr, ETH_ALEN); | ||
705 | rtnl_unlock(); | ||
706 | |||
707 | fc->flogi_progress = 0; | ||
708 | } else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) { | ||
709 | /* | ||
710 | * Save source MAC for point-to-point responses. | ||
711 | */ | ||
712 | memcpy(fc->dest_addr, sa, ETH_ALEN); | ||
713 | fc->address_mode = FCOE_GW_ADDR_MODE; | ||
714 | } | ||
715 | } | ||
716 | |||
717 | /** | ||
718 | * fcoe_watchdog - fcoe timer callback | ||
719 | * @vp: | ||
720 | * | ||
721 | * This checks the pending queue length for fcoe and put fcoe to be paused state | ||
722 | * if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the | ||
723 | * fcoe_hostlist. | ||
724 | * | ||
725 | * Returns: 0 for success | ||
726 | **/ | ||
727 | void fcoe_watchdog(ulong vp) | ||
728 | { | ||
729 | struct fc_lport *lp; | ||
730 | struct fcoe_softc *fc; | ||
731 | int paused = 0; | ||
732 | |||
733 | read_lock(&fcoe_hostlist_lock); | ||
734 | list_for_each_entry(fc, &fcoe_hostlist, list) { | ||
735 | lp = fc->lp; | ||
736 | if (lp) { | ||
737 | if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) | ||
738 | paused = 1; | ||
739 | if (fcoe_check_wait_queue(lp) < FCOE_MAX_QUEUE_DEPTH) { | ||
740 | if (paused) | ||
741 | fc_unpause(lp); | ||
742 | } | ||
743 | } | ||
744 | } | ||
745 | read_unlock(&fcoe_hostlist_lock); | ||
746 | |||
747 | fcoe_timer.expires = jiffies + (1 * HZ); | ||
748 | add_timer(&fcoe_timer); | ||
749 | } | ||
750 | |||
751 | |||
752 | /** | ||
753 | * fcoe_check_wait_queue - put the skb into fcoe pending xmit queue | ||
754 | * @lp: the fc_port for this skb | ||
755 | * @skb: the associated skb to be xmitted | ||
756 | * | ||
757 | * This empties the wait_queue, dequeue the head of the wait_queue queue | ||
758 | * and calls fcoe_start_io() for each packet, if all skb have been | ||
759 | * transmitted, return 0 if a error occurs, then restore wait_queue and | ||
760 | * try again later. | ||
761 | * | ||
762 | * The wait_queue is used when the skb transmit fails. skb will go | ||
763 | * in the wait_queue which will be emptied by the time function OR | ||
764 | * by the next skb transmit. | ||
765 | * | ||
766 | * Returns: 0 for success | ||
767 | **/ | ||
768 | static int fcoe_check_wait_queue(struct fc_lport *lp) | ||
769 | { | ||
770 | int rc, unpause = 0; | ||
771 | int paused = 0; | ||
772 | struct sk_buff *skb; | ||
773 | struct fcoe_softc *fc; | ||
774 | |||
775 | fc = fcoe_softc(lp); | ||
776 | spin_lock_bh(&fc->fcoe_pending_queue.lock); | ||
777 | |||
778 | /* | ||
779 | * is this interface paused? | ||
780 | */ | ||
781 | if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) | ||
782 | paused = 1; | ||
783 | if (fc->fcoe_pending_queue.qlen) { | ||
784 | while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) { | ||
785 | spin_unlock_bh(&fc->fcoe_pending_queue.lock); | ||
786 | rc = fcoe_start_io(skb); | ||
787 | if (rc) { | ||
788 | fcoe_insert_wait_queue_head(lp, skb); | ||
789 | return rc; | ||
790 | } | ||
791 | spin_lock_bh(&fc->fcoe_pending_queue.lock); | ||
792 | } | ||
793 | if (fc->fcoe_pending_queue.qlen < FCOE_MAX_QUEUE_DEPTH) | ||
794 | unpause = 1; | ||
795 | } | ||
796 | spin_unlock_bh(&fc->fcoe_pending_queue.lock); | ||
797 | if ((unpause) && (paused)) | ||
798 | fc_unpause(lp); | ||
799 | return fc->fcoe_pending_queue.qlen; | ||
800 | } | ||
801 | |||
802 | /** | ||
803 | * fcoe_insert_wait_queue_head - puts skb to fcoe pending queue head | ||
804 | * @lp: the fc_port for this skb | ||
805 | * @skb: the associated skb to be xmitted | ||
806 | * | ||
807 | * Returns: none | ||
808 | **/ | ||
809 | static void fcoe_insert_wait_queue_head(struct fc_lport *lp, | ||
810 | struct sk_buff *skb) | ||
811 | { | ||
812 | struct fcoe_softc *fc; | ||
813 | |||
814 | fc = fcoe_softc(lp); | ||
815 | spin_lock_bh(&fc->fcoe_pending_queue.lock); | ||
816 | __skb_queue_head(&fc->fcoe_pending_queue, skb); | ||
817 | spin_unlock_bh(&fc->fcoe_pending_queue.lock); | ||
818 | } | ||
819 | |||
820 | /** | ||
821 | * fcoe_insert_wait_queue - put the skb into fcoe pending queue tail | ||
822 | * @lp: the fc_port for this skb | ||
823 | * @skb: the associated skb to be xmitted | ||
824 | * | ||
825 | * Returns: none | ||
826 | **/ | ||
827 | static void fcoe_insert_wait_queue(struct fc_lport *lp, | ||
828 | struct sk_buff *skb) | ||
829 | { | ||
830 | struct fcoe_softc *fc; | ||
831 | |||
832 | fc = fcoe_softc(lp); | ||
833 | spin_lock_bh(&fc->fcoe_pending_queue.lock); | ||
834 | __skb_queue_tail(&fc->fcoe_pending_queue, skb); | ||
835 | spin_unlock_bh(&fc->fcoe_pending_queue.lock); | ||
836 | } | ||
837 | |||
838 | /** | ||
839 | * fcoe_dev_setup - setup link change notification interface | ||
840 | * | ||
841 | **/ | ||
842 | static void fcoe_dev_setup(void) | ||
843 | { | ||
844 | /* | ||
845 | * here setup a interface specific wd time to | ||
846 | * monitor the link state | ||
847 | */ | ||
848 | register_netdevice_notifier(&fcoe_notifier); | ||
849 | } | ||
850 | |||
851 | /** | ||
852 | * fcoe_dev_setup - cleanup link change notification interface | ||
853 | **/ | ||
854 | static void fcoe_dev_cleanup(void) | ||
855 | { | ||
856 | unregister_netdevice_notifier(&fcoe_notifier); | ||
857 | } | ||
858 | |||
859 | /** | ||
860 | * fcoe_device_notification - netdev event notification callback | ||
861 | * @notifier: context of the notification | ||
862 | * @event: type of event | ||
863 | * @ptr: fixed array for output parsed ifname | ||
864 | * | ||
865 | * This function is called by the ethernet driver in case of link change event | ||
866 | * | ||
867 | * Returns: 0 for success | ||
868 | **/ | ||
869 | static int fcoe_device_notification(struct notifier_block *notifier, | ||
870 | ulong event, void *ptr) | ||
871 | { | ||
872 | struct fc_lport *lp = NULL; | ||
873 | struct net_device *real_dev = ptr; | ||
874 | struct fcoe_softc *fc; | ||
875 | struct fcoe_dev_stats *stats; | ||
876 | u16 new_status; | ||
877 | u32 mfs; | ||
878 | int rc = NOTIFY_OK; | ||
879 | |||
880 | read_lock(&fcoe_hostlist_lock); | ||
881 | list_for_each_entry(fc, &fcoe_hostlist, list) { | ||
882 | if (fc->real_dev == real_dev) { | ||
883 | lp = fc->lp; | ||
884 | break; | ||
885 | } | ||
886 | } | ||
887 | read_unlock(&fcoe_hostlist_lock); | ||
888 | if (lp == NULL) { | ||
889 | rc = NOTIFY_DONE; | ||
890 | goto out; | ||
891 | } | ||
892 | |||
893 | new_status = lp->link_status; | ||
894 | switch (event) { | ||
895 | case NETDEV_DOWN: | ||
896 | case NETDEV_GOING_DOWN: | ||
897 | new_status &= ~FC_LINK_UP; | ||
898 | break; | ||
899 | case NETDEV_UP: | ||
900 | case NETDEV_CHANGE: | ||
901 | new_status &= ~FC_LINK_UP; | ||
902 | if (!fcoe_link_ok(lp)) | ||
903 | new_status |= FC_LINK_UP; | ||
904 | break; | ||
905 | case NETDEV_CHANGEMTU: | ||
906 | mfs = fc->real_dev->mtu - | ||
907 | (sizeof(struct fcoe_hdr) + | ||
908 | sizeof(struct fcoe_crc_eof)); | ||
909 | if (mfs >= FC_MIN_MAX_FRAME) | ||
910 | fc_set_mfs(lp, mfs); | ||
911 | new_status &= ~FC_LINK_UP; | ||
912 | if (!fcoe_link_ok(lp)) | ||
913 | new_status |= FC_LINK_UP; | ||
914 | break; | ||
915 | case NETDEV_REGISTER: | ||
916 | break; | ||
917 | default: | ||
918 | FC_DBG("unknown event %ld call", event); | ||
919 | } | ||
920 | if (lp->link_status != new_status) { | ||
921 | if ((new_status & FC_LINK_UP) == FC_LINK_UP) | ||
922 | fc_linkup(lp); | ||
923 | else { | ||
924 | stats = lp->dev_stats[smp_processor_id()]; | ||
925 | if (stats) | ||
926 | stats->LinkFailureCount++; | ||
927 | fc_linkdown(lp); | ||
928 | fcoe_clean_pending_queue(lp); | ||
929 | } | ||
930 | } | ||
931 | out: | ||
932 | return rc; | ||
933 | } | ||
934 | |||
935 | /** | ||
936 | * fcoe_if_to_netdev - parse a name buffer to get netdev | ||
937 | * @ifname: fixed array for output parsed ifname | ||
938 | * @buffer: incoming buffer to be copied | ||
939 | * | ||
940 | * Returns: NULL or ptr to netdeive | ||
941 | **/ | ||
942 | static struct net_device *fcoe_if_to_netdev(const char *buffer) | ||
943 | { | ||
944 | char *cp; | ||
945 | char ifname[IFNAMSIZ + 2]; | ||
946 | |||
947 | if (buffer) { | ||
948 | strlcpy(ifname, buffer, IFNAMSIZ); | ||
949 | cp = ifname + strlen(ifname); | ||
950 | while (--cp >= ifname && *cp == '\n') | ||
951 | *cp = '\0'; | ||
952 | return dev_get_by_name(&init_net, ifname); | ||
953 | } | ||
954 | return NULL; | ||
955 | } | ||
956 | |||
957 | /** | ||
958 | * fcoe_netdev_to_module_owner - finds out the nic drive moddule of the netdev | ||
959 | * @netdev: the target netdev | ||
960 | * | ||
961 | * Returns: ptr to the struct module, NULL for failure | ||
962 | **/ | ||
963 | static struct module *fcoe_netdev_to_module_owner( | ||
964 | const struct net_device *netdev) | ||
965 | { | ||
966 | struct device *dev; | ||
967 | |||
968 | if (!netdev) | ||
969 | return NULL; | ||
970 | |||
971 | dev = netdev->dev.parent; | ||
972 | if (!dev) | ||
973 | return NULL; | ||
974 | |||
975 | if (!dev->driver) | ||
976 | return NULL; | ||
977 | |||
978 | return dev->driver->owner; | ||
979 | } | ||
980 | |||
981 | /** | ||
982 | * fcoe_ethdrv_get - holds the nic driver module by try_module_get() for | ||
983 | * the corresponding netdev. | ||
984 | * @netdev: the target netdev | ||
985 | * | ||
986 | * Returns: 0 for succsss | ||
987 | **/ | ||
988 | static int fcoe_ethdrv_get(const struct net_device *netdev) | ||
989 | { | ||
990 | struct module *owner; | ||
991 | |||
992 | owner = fcoe_netdev_to_module_owner(netdev); | ||
993 | if (owner) { | ||
994 | printk(KERN_DEBUG "foce:hold driver module %s for %s\n", | ||
995 | owner->name, netdev->name); | ||
996 | return try_module_get(owner); | ||
997 | } | ||
998 | return -ENODEV; | ||
999 | } | ||
1000 | |||
1001 | /** | ||
1002 | * fcoe_ethdrv_get - releases the nic driver module by module_put for | ||
1003 | * the corresponding netdev. | ||
1004 | * @netdev: the target netdev | ||
1005 | * | ||
1006 | * Returns: 0 for succsss | ||
1007 | **/ | ||
1008 | static int fcoe_ethdrv_put(const struct net_device *netdev) | ||
1009 | { | ||
1010 | struct module *owner; | ||
1011 | |||
1012 | owner = fcoe_netdev_to_module_owner(netdev); | ||
1013 | if (owner) { | ||
1014 | printk(KERN_DEBUG "foce:release driver module %s for %s\n", | ||
1015 | owner->name, netdev->name); | ||
1016 | module_put(owner); | ||
1017 | return 0; | ||
1018 | } | ||
1019 | return -ENODEV; | ||
1020 | } | ||
1021 | |||
1022 | /** | ||
1023 | * fcoe_destroy- handles the destroy from sysfs | ||
1024 | * @buffer: expcted to be a eth if name | ||
1025 | * @kp: associated kernel param | ||
1026 | * | ||
1027 | * Returns: 0 for success | ||
1028 | **/ | ||
1029 | static int fcoe_destroy(const char *buffer, struct kernel_param *kp) | ||
1030 | { | ||
1031 | int rc; | ||
1032 | struct net_device *netdev; | ||
1033 | |||
1034 | netdev = fcoe_if_to_netdev(buffer); | ||
1035 | if (!netdev) { | ||
1036 | rc = -ENODEV; | ||
1037 | goto out_nodev; | ||
1038 | } | ||
1039 | /* look for existing lport */ | ||
1040 | if (!fcoe_hostlist_lookup(netdev)) { | ||
1041 | rc = -ENODEV; | ||
1042 | goto out_putdev; | ||
1043 | } | ||
1044 | /* pass to transport */ | ||
1045 | rc = fcoe_transport_release(netdev); | ||
1046 | if (rc) { | ||
1047 | printk(KERN_ERR "fcoe: fcoe_transport_release(%s) failed\n", | ||
1048 | netdev->name); | ||
1049 | rc = -EIO; | ||
1050 | goto out_putdev; | ||
1051 | } | ||
1052 | fcoe_ethdrv_put(netdev); | ||
1053 | rc = 0; | ||
1054 | out_putdev: | ||
1055 | dev_put(netdev); | ||
1056 | out_nodev: | ||
1057 | return rc; | ||
1058 | } | ||
1059 | |||
1060 | /** | ||
1061 | * fcoe_create - handles the create call from sysfs | ||
1062 | * @buffer: expcted to be a eth if name | ||
1063 | * @kp: associated kernel param | ||
1064 | * | ||
1065 | * Returns: 0 for success | ||
1066 | **/ | ||
1067 | static int fcoe_create(const char *buffer, struct kernel_param *kp) | ||
1068 | { | ||
1069 | int rc; | ||
1070 | struct net_device *netdev; | ||
1071 | |||
1072 | netdev = fcoe_if_to_netdev(buffer); | ||
1073 | if (!netdev) { | ||
1074 | rc = -ENODEV; | ||
1075 | goto out_nodev; | ||
1076 | } | ||
1077 | /* look for existing lport */ | ||
1078 | if (fcoe_hostlist_lookup(netdev)) { | ||
1079 | rc = -EEXIST; | ||
1080 | goto out_putdev; | ||
1081 | } | ||
1082 | fcoe_ethdrv_get(netdev); | ||
1083 | |||
1084 | /* pass to transport */ | ||
1085 | rc = fcoe_transport_attach(netdev); | ||
1086 | if (rc) { | ||
1087 | printk(KERN_ERR "fcoe: fcoe_transport_attach(%s) failed\n", | ||
1088 | netdev->name); | ||
1089 | fcoe_ethdrv_put(netdev); | ||
1090 | rc = -EIO; | ||
1091 | goto out_putdev; | ||
1092 | } | ||
1093 | rc = 0; | ||
1094 | out_putdev: | ||
1095 | dev_put(netdev); | ||
1096 | out_nodev: | ||
1097 | return rc; | ||
1098 | } | ||
1099 | |||
1100 | module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR); | ||
1101 | __MODULE_PARM_TYPE(create, "string"); | ||
1102 | MODULE_PARM_DESC(create, "Create fcoe port using net device passed in."); | ||
1103 | module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR); | ||
1104 | __MODULE_PARM_TYPE(destroy, "string"); | ||
1105 | MODULE_PARM_DESC(destroy, "Destroy fcoe port"); | ||
1106 | |||
1107 | /* | ||
1108 | * fcoe_link_ok - check if link is ok for the fc_lport | ||
1109 | * @lp: ptr to the fc_lport | ||
1110 | * | ||
1111 | * Any permanently-disqualifying conditions have been previously checked. | ||
1112 | * This also updates the speed setting, which may change with link for 100/1000. | ||
1113 | * | ||
1114 | * This function should probably be checking for PAUSE support at some point | ||
1115 | * in the future. Currently Per-priority-pause is not determinable using | ||
1116 | * ethtool, so we shouldn't be restrictive until that problem is resolved. | ||
1117 | * | ||
1118 | * Returns: 0 if link is OK for use by FCoE. | ||
1119 | * | ||
1120 | */ | ||
1121 | int fcoe_link_ok(struct fc_lport *lp) | ||
1122 | { | ||
1123 | struct fcoe_softc *fc = fcoe_softc(lp); | ||
1124 | struct net_device *dev = fc->real_dev; | ||
1125 | struct ethtool_cmd ecmd = { ETHTOOL_GSET }; | ||
1126 | int rc = 0; | ||
1127 | |||
1128 | if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) { | ||
1129 | dev = fc->phys_dev; | ||
1130 | if (dev->ethtool_ops->get_settings) { | ||
1131 | dev->ethtool_ops->get_settings(dev, &ecmd); | ||
1132 | lp->link_supported_speeds &= | ||
1133 | ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); | ||
1134 | if (ecmd.supported & (SUPPORTED_1000baseT_Half | | ||
1135 | SUPPORTED_1000baseT_Full)) | ||
1136 | lp->link_supported_speeds |= FC_PORTSPEED_1GBIT; | ||
1137 | if (ecmd.supported & SUPPORTED_10000baseT_Full) | ||
1138 | lp->link_supported_speeds |= | ||
1139 | FC_PORTSPEED_10GBIT; | ||
1140 | if (ecmd.speed == SPEED_1000) | ||
1141 | lp->link_speed = FC_PORTSPEED_1GBIT; | ||
1142 | if (ecmd.speed == SPEED_10000) | ||
1143 | lp->link_speed = FC_PORTSPEED_10GBIT; | ||
1144 | } | ||
1145 | } else | ||
1146 | rc = -1; | ||
1147 | |||
1148 | return rc; | ||
1149 | } | ||
1150 | EXPORT_SYMBOL_GPL(fcoe_link_ok); | ||
1151 | |||
1152 | /* | ||
1153 | * fcoe_percpu_clean - frees skb of the corresponding lport from the per | ||
1154 | * cpu queue. | ||
1155 | * @lp: the fc_lport | ||
1156 | */ | ||
1157 | void fcoe_percpu_clean(struct fc_lport *lp) | ||
1158 | { | ||
1159 | int idx; | ||
1160 | struct fcoe_percpu_s *pp; | ||
1161 | struct fcoe_rcv_info *fr; | ||
1162 | struct sk_buff_head *list; | ||
1163 | struct sk_buff *skb, *next; | ||
1164 | struct sk_buff *head; | ||
1165 | |||
1166 | for (idx = 0; idx < NR_CPUS; idx++) { | ||
1167 | if (fcoe_percpu[idx]) { | ||
1168 | pp = fcoe_percpu[idx]; | ||
1169 | spin_lock_bh(&pp->fcoe_rx_list.lock); | ||
1170 | list = &pp->fcoe_rx_list; | ||
1171 | head = list->next; | ||
1172 | for (skb = head; skb != (struct sk_buff *)list; | ||
1173 | skb = next) { | ||
1174 | next = skb->next; | ||
1175 | fr = fcoe_dev_from_skb(skb); | ||
1176 | if (fr->fr_dev == lp) { | ||
1177 | __skb_unlink(skb, list); | ||
1178 | kfree_skb(skb); | ||
1179 | } | ||
1180 | } | ||
1181 | spin_unlock_bh(&pp->fcoe_rx_list.lock); | ||
1182 | } | ||
1183 | } | ||
1184 | } | ||
1185 | EXPORT_SYMBOL_GPL(fcoe_percpu_clean); | ||
1186 | |||
1187 | /** | ||
1188 | * fcoe_clean_pending_queue - dequeue skb and free it | ||
1189 | * @lp: the corresponding fc_lport | ||
1190 | * | ||
1191 | * Returns: none | ||
1192 | **/ | ||
1193 | void fcoe_clean_pending_queue(struct fc_lport *lp) | ||
1194 | { | ||
1195 | struct fcoe_softc *fc = lport_priv(lp); | ||
1196 | struct sk_buff *skb; | ||
1197 | |||
1198 | spin_lock_bh(&fc->fcoe_pending_queue.lock); | ||
1199 | while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) { | ||
1200 | spin_unlock_bh(&fc->fcoe_pending_queue.lock); | ||
1201 | kfree_skb(skb); | ||
1202 | spin_lock_bh(&fc->fcoe_pending_queue.lock); | ||
1203 | } | ||
1204 | spin_unlock_bh(&fc->fcoe_pending_queue.lock); | ||
1205 | } | ||
1206 | EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue); | ||
1207 | |||
1208 | /** | ||
1209 | * libfc_host_alloc - allocate a Scsi_Host with room for the fc_lport | ||
1210 | * @sht: ptr to the scsi host templ | ||
1211 | * @priv_size: size of private data after fc_lport | ||
1212 | * | ||
1213 | * Returns: ptr to Scsi_Host | ||
1214 | * TODO - to libfc? | ||
1215 | */ | ||
1216 | static inline struct Scsi_Host *libfc_host_alloc( | ||
1217 | struct scsi_host_template *sht, int priv_size) | ||
1218 | { | ||
1219 | return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size); | ||
1220 | } | ||
1221 | |||
1222 | /** | ||
1223 | * fcoe_host_alloc - allocate a Scsi_Host with room for the fcoe_softc | ||
1224 | * @sht: ptr to the scsi host templ | ||
1225 | * @priv_size: size of private data after fc_lport | ||
1226 | * | ||
1227 | * Returns: ptr to Scsi_Host | ||
1228 | */ | ||
1229 | struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *sht, int priv_size) | ||
1230 | { | ||
1231 | return libfc_host_alloc(sht, sizeof(struct fcoe_softc) + priv_size); | ||
1232 | } | ||
1233 | EXPORT_SYMBOL_GPL(fcoe_host_alloc); | ||
1234 | |||
1235 | /* | ||
1236 | * fcoe_reset - resets the fcoe | ||
1237 | * @shost: shost the reset is from | ||
1238 | * | ||
1239 | * Returns: always 0 | ||
1240 | */ | ||
1241 | int fcoe_reset(struct Scsi_Host *shost) | ||
1242 | { | ||
1243 | struct fc_lport *lport = shost_priv(shost); | ||
1244 | fc_lport_reset(lport); | ||
1245 | return 0; | ||
1246 | } | ||
1247 | EXPORT_SYMBOL_GPL(fcoe_reset); | ||
1248 | |||
1249 | /* | ||
1250 | * fcoe_wwn_from_mac - converts 48-bit IEEE MAC address to 64-bit FC WWN. | ||
1251 | * @mac: mac address | ||
1252 | * @scheme: check port | ||
1253 | * @port: port indicator for converting | ||
1254 | * | ||
1255 | * Returns: u64 fc world wide name | ||
1256 | */ | ||
1257 | u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN], | ||
1258 | unsigned int scheme, unsigned int port) | ||
1259 | { | ||
1260 | u64 wwn; | ||
1261 | u64 host_mac; | ||
1262 | |||
1263 | /* The MAC is in NO, so flip only the low 48 bits */ | ||
1264 | host_mac = ((u64) mac[0] << 40) | | ||
1265 | ((u64) mac[1] << 32) | | ||
1266 | ((u64) mac[2] << 24) | | ||
1267 | ((u64) mac[3] << 16) | | ||
1268 | ((u64) mac[4] << 8) | | ||
1269 | (u64) mac[5]; | ||
1270 | |||
1271 | WARN_ON(host_mac >= (1ULL << 48)); | ||
1272 | wwn = host_mac | ((u64) scheme << 60); | ||
1273 | switch (scheme) { | ||
1274 | case 1: | ||
1275 | WARN_ON(port != 0); | ||
1276 | break; | ||
1277 | case 2: | ||
1278 | WARN_ON(port >= 0xfff); | ||
1279 | wwn |= (u64) port << 48; | ||
1280 | break; | ||
1281 | default: | ||
1282 | WARN_ON(1); | ||
1283 | break; | ||
1284 | } | ||
1285 | |||
1286 | return wwn; | ||
1287 | } | ||
1288 | EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac); | ||
1289 | /* | ||
1290 | * fcoe_hostlist_lookup_softc - find the corresponding lport by a given device | ||
1291 | * @device: this is currently ptr to net_device | ||
1292 | * | ||
1293 | * Returns: NULL or the located fcoe_softc | ||
1294 | */ | ||
1295 | static struct fcoe_softc *fcoe_hostlist_lookup_softc( | ||
1296 | const struct net_device *dev) | ||
1297 | { | ||
1298 | struct fcoe_softc *fc; | ||
1299 | |||
1300 | read_lock(&fcoe_hostlist_lock); | ||
1301 | list_for_each_entry(fc, &fcoe_hostlist, list) { | ||
1302 | if (fc->real_dev == dev) { | ||
1303 | read_unlock(&fcoe_hostlist_lock); | ||
1304 | return fc; | ||
1305 | } | ||
1306 | } | ||
1307 | read_unlock(&fcoe_hostlist_lock); | ||
1308 | return NULL; | ||
1309 | } | ||
1310 | |||
1311 | /* | ||
1312 | * fcoe_hostlist_lookup - find the corresponding lport by netdev | ||
1313 | * @netdev: ptr to net_device | ||
1314 | * | ||
1315 | * Returns: 0 for success | ||
1316 | */ | ||
1317 | struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) | ||
1318 | { | ||
1319 | struct fcoe_softc *fc; | ||
1320 | |||
1321 | fc = fcoe_hostlist_lookup_softc(netdev); | ||
1322 | |||
1323 | return (fc) ? fc->lp : NULL; | ||
1324 | } | ||
1325 | EXPORT_SYMBOL_GPL(fcoe_hostlist_lookup); | ||
1326 | |||
1327 | /* | ||
1328 | * fcoe_hostlist_add - add a lport to lports list | ||
1329 | * @lp: ptr to the fc_lport to badded | ||
1330 | * | ||
1331 | * Returns: 0 for success | ||
1332 | */ | ||
1333 | int fcoe_hostlist_add(const struct fc_lport *lp) | ||
1334 | { | ||
1335 | struct fcoe_softc *fc; | ||
1336 | |||
1337 | fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp)); | ||
1338 | if (!fc) { | ||
1339 | fc = fcoe_softc(lp); | ||
1340 | write_lock_bh(&fcoe_hostlist_lock); | ||
1341 | list_add_tail(&fc->list, &fcoe_hostlist); | ||
1342 | write_unlock_bh(&fcoe_hostlist_lock); | ||
1343 | } | ||
1344 | return 0; | ||
1345 | } | ||
1346 | EXPORT_SYMBOL_GPL(fcoe_hostlist_add); | ||
1347 | |||
1348 | /* | ||
1349 | * fcoe_hostlist_remove - remove a lport from lports list | ||
1350 | * @lp: ptr to the fc_lport to badded | ||
1351 | * | ||
1352 | * Returns: 0 for success | ||
1353 | */ | ||
1354 | int fcoe_hostlist_remove(const struct fc_lport *lp) | ||
1355 | { | ||
1356 | struct fcoe_softc *fc; | ||
1357 | |||
1358 | fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp)); | ||
1359 | BUG_ON(!fc); | ||
1360 | write_lock_bh(&fcoe_hostlist_lock); | ||
1361 | list_del(&fc->list); | ||
1362 | write_unlock_bh(&fcoe_hostlist_lock); | ||
1363 | |||
1364 | return 0; | ||
1365 | } | ||
1366 | EXPORT_SYMBOL_GPL(fcoe_hostlist_remove); | ||
1367 | |||
1368 | /** | ||
1369 | * fcoe_libfc_config - sets up libfc related properties for lport | ||
1370 | * @lp: ptr to the fc_lport | ||
1371 | * @tt: libfc function template | ||
1372 | * | ||
1373 | * Returns : 0 for success | ||
1374 | **/ | ||
1375 | int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt) | ||
1376 | { | ||
1377 | /* Set the function pointers set by the LLDD */ | ||
1378 | memcpy(&lp->tt, tt, sizeof(*tt)); | ||
1379 | if (fc_fcp_init(lp)) | ||
1380 | return -ENOMEM; | ||
1381 | fc_exch_init(lp); | ||
1382 | fc_elsct_init(lp); | ||
1383 | fc_lport_init(lp); | ||
1384 | fc_rport_init(lp); | ||
1385 | fc_disc_init(lp); | ||
1386 | |||
1387 | return 0; | ||
1388 | } | ||
1389 | EXPORT_SYMBOL_GPL(fcoe_libfc_config); | ||
1390 | |||
1391 | /** | ||
1392 | * fcoe_init - fcoe module loading initialization | ||
1393 | * | ||
1394 | * Initialization routine | ||
1395 | * 1. Will create fc transport software structure | ||
1396 | * 2. initialize the link list of port information structure | ||
1397 | * | ||
1398 | * Returns 0 on success, negative on failure | ||
1399 | **/ | ||
1400 | static int __init fcoe_init(void) | ||
1401 | { | ||
1402 | int cpu; | ||
1403 | struct fcoe_percpu_s *p; | ||
1404 | |||
1405 | |||
1406 | INIT_LIST_HEAD(&fcoe_hostlist); | ||
1407 | rwlock_init(&fcoe_hostlist_lock); | ||
1408 | |||
1409 | #ifdef CONFIG_HOTPLUG_CPU | ||
1410 | register_cpu_notifier(&fcoe_cpu_notifier); | ||
1411 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
1412 | |||
1413 | /* | ||
1414 | * initialize per CPU interrupt thread | ||
1415 | */ | ||
1416 | for_each_online_cpu(cpu) { | ||
1417 | p = kzalloc(sizeof(struct fcoe_percpu_s), GFP_KERNEL); | ||
1418 | if (p) { | ||
1419 | p->thread = kthread_create(fcoe_percpu_receive_thread, | ||
1420 | (void *)p, | ||
1421 | "fcoethread/%d", cpu); | ||
1422 | |||
1423 | /* | ||
1424 | * if there is no error then bind the thread to the cpu | ||
1425 | * initialize the semaphore and skb queue head | ||
1426 | */ | ||
1427 | if (likely(!IS_ERR(p->thread))) { | ||
1428 | p->cpu = cpu; | ||
1429 | fcoe_percpu[cpu] = p; | ||
1430 | skb_queue_head_init(&p->fcoe_rx_list); | ||
1431 | kthread_bind(p->thread, cpu); | ||
1432 | wake_up_process(p->thread); | ||
1433 | } else { | ||
1434 | fcoe_percpu[cpu] = NULL; | ||
1435 | kfree(p); | ||
1436 | |||
1437 | } | ||
1438 | } | ||
1439 | } | ||
1440 | |||
1441 | /* | ||
1442 | * setup link change notification | ||
1443 | */ | ||
1444 | fcoe_dev_setup(); | ||
1445 | |||
1446 | init_timer(&fcoe_timer); | ||
1447 | fcoe_timer.data = 0; | ||
1448 | fcoe_timer.function = fcoe_watchdog; | ||
1449 | fcoe_timer.expires = (jiffies + (10 * HZ)); | ||
1450 | add_timer(&fcoe_timer); | ||
1451 | |||
1452 | /* initiatlize the fcoe transport */ | ||
1453 | fcoe_transport_init(); | ||
1454 | |||
1455 | fcoe_sw_init(); | ||
1456 | |||
1457 | return 0; | ||
1458 | } | ||
1459 | module_init(fcoe_init); | ||
1460 | |||
1461 | /** | ||
1462 | * fcoe_exit - fcoe module unloading cleanup | ||
1463 | * | ||
1464 | * Returns 0 on success, negative on failure | ||
1465 | **/ | ||
1466 | static void __exit fcoe_exit(void) | ||
1467 | { | ||
1468 | u32 idx; | ||
1469 | struct fcoe_softc *fc, *tmp; | ||
1470 | struct fcoe_percpu_s *p; | ||
1471 | struct sk_buff *skb; | ||
1472 | |||
1473 | /* | ||
1474 | * Stop all call back interfaces | ||
1475 | */ | ||
1476 | #ifdef CONFIG_HOTPLUG_CPU | ||
1477 | unregister_cpu_notifier(&fcoe_cpu_notifier); | ||
1478 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
1479 | fcoe_dev_cleanup(); | ||
1480 | |||
1481 | /* | ||
1482 | * stop timer | ||
1483 | */ | ||
1484 | del_timer_sync(&fcoe_timer); | ||
1485 | |||
1486 | /* releases the assocaited fcoe transport for each lport */ | ||
1487 | list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list) | ||
1488 | fcoe_transport_release(fc->real_dev); | ||
1489 | |||
1490 | for (idx = 0; idx < NR_CPUS; idx++) { | ||
1491 | if (fcoe_percpu[idx]) { | ||
1492 | kthread_stop(fcoe_percpu[idx]->thread); | ||
1493 | p = fcoe_percpu[idx]; | ||
1494 | spin_lock_bh(&p->fcoe_rx_list.lock); | ||
1495 | while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL) | ||
1496 | kfree_skb(skb); | ||
1497 | spin_unlock_bh(&p->fcoe_rx_list.lock); | ||
1498 | if (fcoe_percpu[idx]->crc_eof_page) | ||
1499 | put_page(fcoe_percpu[idx]->crc_eof_page); | ||
1500 | kfree(fcoe_percpu[idx]); | ||
1501 | } | ||
1502 | } | ||
1503 | |||
1504 | /* remove sw trasnport */ | ||
1505 | fcoe_sw_exit(); | ||
1506 | |||
1507 | /* detach the transport */ | ||
1508 | fcoe_transport_exit(); | ||
1509 | } | ||
1510 | module_exit(fcoe_exit); | ||
diff --git a/include/scsi/fc_transport_fcoe.h b/include/scsi/fc_transport_fcoe.h new file mode 100644 index 000000000000..8dca2af14ffc --- /dev/null +++ b/include/scsi/fc_transport_fcoe.h | |||
@@ -0,0 +1,54 @@ | |||
1 | #ifndef FC_TRANSPORT_FCOE_H | ||
2 | #define FC_TRANSPORT_FCOE_H | ||
3 | |||
4 | #include <linux/device.h> | ||
5 | #include <linux/netdevice.h> | ||
6 | #include <scsi/scsi_host.h> | ||
7 | #include <scsi/libfc.h> | ||
8 | |||
9 | /** | ||
10 | * struct fcoe_transport - FCoE transport struct for generic transport | ||
11 | * for Ethernet devices as well as pure HBAs | ||
12 | * | ||
13 | * @name: name for thsi transport | ||
14 | * @bus: physical bus type (pci_bus_type) | ||
15 | * @driver: physical bus driver for network device | ||
16 | * @create: entry create function | ||
17 | * @destroy: exit destroy function | ||
18 | * @list: list of transports | ||
19 | */ | ||
20 | struct fcoe_transport { | ||
21 | char *name; | ||
22 | unsigned short vendor; | ||
23 | unsigned short device; | ||
24 | struct bus_type *bus; | ||
25 | struct device_driver *driver; | ||
26 | int (*create)(struct net_device *device); | ||
27 | int (*destroy)(struct net_device *device); | ||
28 | bool (*match)(struct net_device *device); | ||
29 | struct list_head list; | ||
30 | struct list_head devlist; | ||
31 | struct mutex devlock; | ||
32 | }; | ||
33 | |||
34 | /** | ||
35 | * MODULE_ALIAS_FCOE_PCI | ||
36 | * | ||
37 | * some care must be taken with this, vendor and device MUST be a hex value | ||
38 | * preceded with 0x and with letters in lower case (0x12ab, not 0x12AB or 12AB) | ||
39 | */ | ||
40 | #define MODULE_ALIAS_FCOE_PCI(vendor, device) \ | ||
41 | MODULE_ALIAS("fcoe-pci-" __stringify(vendor) "-" __stringify(device)) | ||
42 | |||
43 | /* exported funcs */ | ||
44 | int fcoe_transport_attach(struct net_device *netdev); | ||
45 | int fcoe_transport_release(struct net_device *netdev); | ||
46 | int fcoe_transport_register(struct fcoe_transport *t); | ||
47 | int fcoe_transport_unregister(struct fcoe_transport *t); | ||
48 | int fcoe_load_transport_driver(struct net_device *netdev); | ||
49 | int __init fcoe_transport_init(void); | ||
50 | int __exit fcoe_transport_exit(void); | ||
51 | |||
52 | /* fcow_sw is the default transport */ | ||
53 | extern struct fcoe_transport fcoe_sw_transport; | ||
54 | #endif /* FC_TRANSPORT_FCOE_H */ | ||
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h new file mode 100644 index 000000000000..89fdbb9a6a1b --- /dev/null +++ b/include/scsi/libfcoe.h | |||
@@ -0,0 +1,176 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2007 - 2008 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 | #ifndef _LIBFCOE_H | ||
21 | #define _LIBFCOE_H | ||
22 | |||
23 | #include <linux/netdevice.h> | ||
24 | #include <linux/skbuff.h> | ||
25 | #include <scsi/fc/fc_fcoe.h> | ||
26 | #include <scsi/libfc.h> | ||
27 | |||
28 | /* | ||
29 | * this percpu struct for fcoe | ||
30 | */ | ||
31 | struct fcoe_percpu_s { | ||
32 | int cpu; | ||
33 | struct task_struct *thread; | ||
34 | struct sk_buff_head fcoe_rx_list; | ||
35 | struct page *crc_eof_page; | ||
36 | int crc_eof_offset; | ||
37 | }; | ||
38 | |||
39 | /* | ||
40 | * the fcoe sw transport private data | ||
41 | */ | ||
42 | struct fcoe_softc { | ||
43 | struct list_head list; | ||
44 | struct fc_lport *lp; | ||
45 | struct net_device *real_dev; | ||
46 | struct net_device *phys_dev; /* device with ethtool_ops */ | ||
47 | struct packet_type fcoe_packet_type; | ||
48 | struct sk_buff_head fcoe_pending_queue; | ||
49 | |||
50 | u8 dest_addr[ETH_ALEN]; | ||
51 | u8 ctl_src_addr[ETH_ALEN]; | ||
52 | u8 data_src_addr[ETH_ALEN]; | ||
53 | /* | ||
54 | * fcoe protocol address learning related stuff | ||
55 | */ | ||
56 | u16 flogi_oxid; | ||
57 | u8 flogi_progress; | ||
58 | u8 address_mode; | ||
59 | }; | ||
60 | |||
61 | static inline struct fcoe_softc *fcoe_softc( | ||
62 | const struct fc_lport *lp) | ||
63 | { | ||
64 | return (struct fcoe_softc *)lport_priv(lp); | ||
65 | } | ||
66 | |||
67 | static inline struct net_device *fcoe_netdev( | ||
68 | const struct fc_lport *lp) | ||
69 | { | ||
70 | return fcoe_softc(lp)->real_dev; | ||
71 | } | ||
72 | |||
73 | static inline struct fcoe_hdr *skb_fcoe_header(const struct sk_buff *skb) | ||
74 | { | ||
75 | return (struct fcoe_hdr *)skb_network_header(skb); | ||
76 | } | ||
77 | |||
78 | static inline int skb_fcoe_offset(const struct sk_buff *skb) | ||
79 | { | ||
80 | return skb_network_offset(skb); | ||
81 | } | ||
82 | |||
83 | static inline struct fc_frame_header *skb_fc_header(const struct sk_buff *skb) | ||
84 | { | ||
85 | return (struct fc_frame_header *)skb_transport_header(skb); | ||
86 | } | ||
87 | |||
88 | static inline int skb_fc_offset(const struct sk_buff *skb) | ||
89 | { | ||
90 | return skb_transport_offset(skb); | ||
91 | } | ||
92 | |||
93 | static inline void skb_reset_fc_header(struct sk_buff *skb) | ||
94 | { | ||
95 | skb_reset_network_header(skb); | ||
96 | skb_set_transport_header(skb, skb_network_offset(skb) + | ||
97 | sizeof(struct fcoe_hdr)); | ||
98 | } | ||
99 | |||
100 | static inline bool skb_fc_is_data(const struct sk_buff *skb) | ||
101 | { | ||
102 | return skb_fc_header(skb)->fh_r_ctl == FC_RCTL_DD_SOL_DATA; | ||
103 | } | ||
104 | |||
105 | static inline bool skb_fc_is_cmd(const struct sk_buff *skb) | ||
106 | { | ||
107 | return skb_fc_header(skb)->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD; | ||
108 | } | ||
109 | |||
110 | static inline bool skb_fc_has_exthdr(const struct sk_buff *skb) | ||
111 | { | ||
112 | return (skb_fc_header(skb)->fh_r_ctl == FC_RCTL_VFTH) || | ||
113 | (skb_fc_header(skb)->fh_r_ctl == FC_RCTL_IFRH) || | ||
114 | (skb_fc_header(skb)->fh_r_ctl == FC_RCTL_ENCH); | ||
115 | } | ||
116 | |||
117 | static inline bool skb_fc_is_roff(const struct sk_buff *skb) | ||
118 | { | ||
119 | return skb_fc_header(skb)->fh_f_ctl[2] & FC_FC_REL_OFF; | ||
120 | } | ||
121 | |||
122 | static inline u16 skb_fc_oxid(const struct sk_buff *skb) | ||
123 | { | ||
124 | return be16_to_cpu(skb_fc_header(skb)->fh_ox_id); | ||
125 | } | ||
126 | |||
127 | static inline u16 skb_fc_rxid(const struct sk_buff *skb) | ||
128 | { | ||
129 | return be16_to_cpu(skb_fc_header(skb)->fh_rx_id); | ||
130 | } | ||
131 | |||
132 | /* FIXME - DMA_BIDIRECTIONAL ? */ | ||
133 | #define skb_cb(skb) ((struct fcoe_rcv_info *)&((skb)->cb[0])) | ||
134 | #define skb_cmd(skb) (skb_cb(skb)->fr_cmd) | ||
135 | #define skb_dir(skb) (skb_cmd(skb)->sc_data_direction) | ||
136 | static inline bool skb_fc_is_read(const struct sk_buff *skb) | ||
137 | { | ||
138 | if (skb_fc_is_cmd(skb) && skb_cmd(skb)) | ||
139 | return skb_dir(skb) == DMA_FROM_DEVICE; | ||
140 | return false; | ||
141 | } | ||
142 | |||
143 | static inline bool skb_fc_is_write(const struct sk_buff *skb) | ||
144 | { | ||
145 | if (skb_fc_is_cmd(skb) && skb_cmd(skb)) | ||
146 | return skb_dir(skb) == DMA_TO_DEVICE; | ||
147 | return false; | ||
148 | } | ||
149 | |||
150 | /* libfcoe funcs */ | ||
151 | int fcoe_reset(struct Scsi_Host *shost); | ||
152 | u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN], | ||
153 | unsigned int scheme, unsigned int port); | ||
154 | |||
155 | u32 fcoe_fc_crc(struct fc_frame *fp); | ||
156 | int fcoe_xmit(struct fc_lport *, struct fc_frame *); | ||
157 | int fcoe_rcv(struct sk_buff *, struct net_device *, | ||
158 | struct packet_type *, struct net_device *); | ||
159 | |||
160 | int fcoe_percpu_receive_thread(void *arg); | ||
161 | void fcoe_clean_pending_queue(struct fc_lport *lp); | ||
162 | void fcoe_percpu_clean(struct fc_lport *lp); | ||
163 | void fcoe_watchdog(ulong vp); | ||
164 | int fcoe_link_ok(struct fc_lport *lp); | ||
165 | |||
166 | struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); | ||
167 | int fcoe_hostlist_add(const struct fc_lport *); | ||
168 | int fcoe_hostlist_remove(const struct fc_lport *); | ||
169 | |||
170 | struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *, int); | ||
171 | int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *); | ||
172 | |||
173 | /* fcoe sw hba */ | ||
174 | int __init fcoe_sw_init(void); | ||
175 | int __exit fcoe_sw_exit(void); | ||
176 | #endif /* _LIBFCOE_H */ | ||