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 /drivers/scsi/fcoe/fc_transport_fcoe.c | |
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>
Diffstat (limited to 'drivers/scsi/fcoe/fc_transport_fcoe.c')
-rw-r--r-- | drivers/scsi/fcoe/fc_transport_fcoe.c | 446 |
1 files changed, 446 insertions, 0 deletions
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 | } | ||