]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/scsi/fcoe/fc_transport_fcoe.c
Merge branch 'for-linus' of git://git.o-hand.com/linux-rpurdie-backlight
[net-next-2.6.git] / drivers / scsi / fcoe / fc_transport_fcoe.c
CommitLineData
85b4aa49
RL
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 */
25struct 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 */
32static LIST_HEAD(fcoe_transports);
33static DEFINE_MUTEX(fcoe_transports_lock);
34
35/**
34f42a07
RL
36 * fcoe_transport_default() - Returns ptr to the default transport fcoe_sw
37 */
85b4aa49
RL
38struct fcoe_transport *fcoe_transport_default(void)
39{
40 return &fcoe_sw_transport;
41}
42
43/**
34f42a07 44 * fcoe_transport_to_pcidev() - get the pci dev from a netdev
85b4aa49
RL
45 * @netdev: the netdev that pci dev will be retrived from
46 *
47 * Returns: NULL or the corrsponding pci_dev
34f42a07 48 */
85b4aa49
RL
49struct 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/**
34f42a07 57 * fcoe_transport_device_lookup() - Lookup a transport
85b4aa49
RL
58 * @netdev: the netdev the transport to be attached to
59 *
60 * This will look for existing offload driver, if not found, it falls back to
61 * the default sw hba (fcoe_sw) as its fcoe transport.
62 *
63 * Returns: 0 for success
34f42a07 64 */
b2ab99c9
RL
65static struct fcoe_transport_internal *
66fcoe_transport_device_lookup(struct fcoe_transport *t,
67 struct net_device *netdev)
85b4aa49
RL
68{
69 struct fcoe_transport_internal *ti;
70
71 /* assign the transpor to this device */
72 mutex_lock(&t->devlock);
73 list_for_each_entry(ti, &t->devlist, list) {
74 if (ti->netdev == netdev) {
75 mutex_unlock(&t->devlock);
76 return ti;
77 }
78 }
79 mutex_unlock(&t->devlock);
80 return NULL;
81}
82/**
34f42a07 83 * fcoe_transport_device_add() - Assign a transport to a device
85b4aa49
RL
84 * @netdev: the netdev the transport to be attached to
85 *
86 * This will look for existing offload driver, if not found, it falls back to
87 * the default sw hba (fcoe_sw) as its fcoe transport.
88 *
89 * Returns: 0 for success
34f42a07 90 */
85b4aa49
RL
91static int fcoe_transport_device_add(struct fcoe_transport *t,
92 struct net_device *netdev)
93{
94 struct fcoe_transport_internal *ti;
95
96 ti = fcoe_transport_device_lookup(t, netdev);
97 if (ti) {
98 printk(KERN_DEBUG "fcoe_transport_device_add:"
99 "device %s is already added to transport %s\n",
100 netdev->name, t->name);
101 return -EEXIST;
102 }
103 /* allocate an internal struct to host the netdev and the list */
104 ti = kzalloc(sizeof(*ti), GFP_KERNEL);
105 if (!ti)
106 return -ENOMEM;
107
108 ti->t = t;
109 ti->netdev = netdev;
110 INIT_LIST_HEAD(&ti->list);
111 dev_hold(ti->netdev);
112
113 mutex_lock(&t->devlock);
114 list_add(&ti->list, &t->devlist);
115 mutex_unlock(&t->devlock);
116
117 printk(KERN_DEBUG "fcoe_transport_device_add:"
118 "device %s added to transport %s\n",
119 netdev->name, t->name);
120
121 return 0;
122}
123
124/**
34f42a07 125 * fcoe_transport_device_remove() - Remove a device from its transport
85b4aa49
RL
126 * @netdev: the netdev the transport to be attached to
127 *
34f42a07 128 * This removes the device from the transport so the given transport will
85b4aa49
RL
129 * not manage this device any more
130 *
131 * Returns: 0 for success
34f42a07 132 */
85b4aa49
RL
133static int fcoe_transport_device_remove(struct fcoe_transport *t,
134 struct net_device *netdev)
135{
136 struct fcoe_transport_internal *ti;
137
138 ti = fcoe_transport_device_lookup(t, netdev);
139 if (!ti) {
140 printk(KERN_DEBUG "fcoe_transport_device_remove:"
141 "device %s is not managed by transport %s\n",
142 netdev->name, t->name);
143 return -ENODEV;
144 }
145 mutex_lock(&t->devlock);
146 list_del(&ti->list);
147 mutex_unlock(&t->devlock);
148 printk(KERN_DEBUG "fcoe_transport_device_remove:"
149 "device %s removed from transport %s\n",
150 netdev->name, t->name);
151 dev_put(ti->netdev);
152 kfree(ti);
153 return 0;
154}
155
156/**
34f42a07 157 * fcoe_transport_device_remove_all() - Remove all from transport devlist
85b4aa49 158 *
34f42a07 159 * This removes the device from the transport so the given transport will
85b4aa49
RL
160 * not manage this device any more
161 *
162 * Returns: 0 for success
34f42a07 163 */
85b4aa49
RL
164static void fcoe_transport_device_remove_all(struct fcoe_transport *t)
165{
166 struct fcoe_transport_internal *ti, *tmp;
167
168 mutex_lock(&t->devlock);
169 list_for_each_entry_safe(ti, tmp, &t->devlist, list) {
170 list_del(&ti->list);
171 kfree(ti);
172 }
173 mutex_unlock(&t->devlock);
174}
175
176/**
34f42a07
RL
177 * fcoe_transport_match() - Use the bus device match function to match the hw
178 * @t: The fcoe transport to check
179 * @netdev: The netdev to match against
85b4aa49 180 *
34f42a07 181 * This function is used to check if the given transport wants to manage the
85b4aa49
RL
182 * input netdev. if the transports implements the match function, it will be
183 * called, o.w. we just compare the pci vendor and device id.
184 *
185 * Returns: true for match up
34f42a07 186 */
85b4aa49 187static bool fcoe_transport_match(struct fcoe_transport *t,
b2ab99c9 188 struct net_device *netdev)
85b4aa49
RL
189{
190 /* match transport by vendor and device id */
191 struct pci_dev *pci;
192
193 pci = fcoe_transport_pcidev(netdev);
194
195 if (pci) {
196 printk(KERN_DEBUG "fcoe_transport_match:"
197 "%s:%x:%x -- %s:%x:%x\n",
198 t->name, t->vendor, t->device,
199 netdev->name, pci->vendor, pci->device);
200
201 /* if transport supports match */
202 if (t->match)
203 return t->match(netdev);
204
205 /* else just compare the vendor and device id: pci only */
206 return (t->vendor == pci->vendor) && (t->device == pci->device);
207 }
208 return false;
209}
210
211/**
34f42a07 212 * fcoe_transport_lookup() - Check if the transport is already registered
85b4aa49
RL
213 * @t: the transport to be looked up
214 *
215 * This compares the parent device (pci) vendor and device id
216 *
217 * Returns: NULL if not found
218 *
34f42a07
RL
219 * TODO: return default sw transport if no other transport is found
220 */
b2ab99c9
RL
221static struct fcoe_transport *
222fcoe_transport_lookup(struct net_device *netdev)
85b4aa49
RL
223{
224 struct fcoe_transport *t;
225
226 mutex_lock(&fcoe_transports_lock);
227 list_for_each_entry(t, &fcoe_transports, list) {
228 if (fcoe_transport_match(t, netdev)) {
229 mutex_unlock(&fcoe_transports_lock);
230 return t;
231 }
232 }
233 mutex_unlock(&fcoe_transports_lock);
234
235 printk(KERN_DEBUG "fcoe_transport_lookup:"
236 "use default transport for %s\n", netdev->name);
237 return fcoe_transport_default();
238}
239
240/**
34f42a07 241 * fcoe_transport_register() - Adds a fcoe transport to the fcoe transports list
85b4aa49
RL
242 * @t: ptr to the fcoe transport to be added
243 *
244 * Returns: 0 for success
34f42a07 245 */
85b4aa49
RL
246int fcoe_transport_register(struct fcoe_transport *t)
247{
248 struct fcoe_transport *tt;
249
250 /* TODO - add fcoe_transport specific initialization here */
251 mutex_lock(&fcoe_transports_lock);
252 list_for_each_entry(tt, &fcoe_transports, list) {
253 if (tt == t) {
254 mutex_unlock(&fcoe_transports_lock);
255 return -EEXIST;
256 }
257 }
258 list_add_tail(&t->list, &fcoe_transports);
259 mutex_unlock(&fcoe_transports_lock);
260
85b4aa49
RL
261 printk(KERN_DEBUG "fcoe_transport_register:%s\n", t->name);
262
263 return 0;
264}
265EXPORT_SYMBOL_GPL(fcoe_transport_register);
266
267/**
34f42a07 268 * fcoe_transport_unregister() - Remove the tranport fro the fcoe transports list
85b4aa49
RL
269 * @t: ptr to the fcoe transport to be removed
270 *
271 * Returns: 0 for success
34f42a07 272 */
85b4aa49
RL
273int fcoe_transport_unregister(struct fcoe_transport *t)
274{
275 struct fcoe_transport *tt, *tmp;
276
277 mutex_lock(&fcoe_transports_lock);
278 list_for_each_entry_safe(tt, tmp, &fcoe_transports, list) {
279 if (tt == t) {
280 list_del(&t->list);
281 mutex_unlock(&fcoe_transports_lock);
282 fcoe_transport_device_remove_all(t);
283 printk(KERN_DEBUG "fcoe_transport_unregister:%s\n",
284 t->name);
285 return 0;
286 }
287 }
288 mutex_unlock(&fcoe_transports_lock);
289 return -ENODEV;
290}
291EXPORT_SYMBOL_GPL(fcoe_transport_unregister);
292
34f42a07
RL
293/**
294 * fcoe_load_transport_driver() - Load an offload driver by alias name
85b4aa49
RL
295 * @netdev: the target net device
296 *
297 * Requests for an offload driver module as the fcoe transport, if fails, it
298 * falls back to use the SW HBA (fcoe_sw) as its transport
299 *
300 * TODO -
301 * 1. supports only PCI device
302 * 2. needs fix for VLAn and bonding
303 * 3. pure hw fcoe hba may not have netdev
304 *
305 * Returns: 0 for success
34f42a07 306 */
85b4aa49
RL
307int fcoe_load_transport_driver(struct net_device *netdev)
308{
309 struct pci_dev *pci;
310 struct device *dev = netdev->dev.parent;
311
312 if (fcoe_transport_lookup(netdev)) {
313 /* load default transport */
314 printk(KERN_DEBUG "fcoe: already loaded transport for %s\n",
315 netdev->name);
316 return -EEXIST;
317 }
318
319 pci = to_pci_dev(dev);
320 if (dev->bus != &pci_bus_type) {
321 printk(KERN_DEBUG "fcoe: support noly PCI device\n");
322 return -ENODEV;
323 }
324 printk(KERN_DEBUG "fcoe: loading driver fcoe-pci-0x%04x-0x%04x\n",
325 pci->vendor, pci->device);
326
327 return request_module("fcoe-pci-0x%04x-0x%04x",
328 pci->vendor, pci->device);
329
330}
331EXPORT_SYMBOL_GPL(fcoe_load_transport_driver);
332
333/**
34f42a07 334 * fcoe_transport_attach() - Load transport to fcoe
85b4aa49
RL
335 * @netdev: the netdev the transport to be attached to
336 *
337 * This will look for existing offload driver, if not found, it falls back to
338 * the default sw hba (fcoe_sw) as its fcoe transport.
339 *
340 * Returns: 0 for success
34f42a07 341 */
85b4aa49
RL
342int fcoe_transport_attach(struct net_device *netdev)
343{
344 struct fcoe_transport *t;
345
346 /* find the corresponding transport */
347 t = fcoe_transport_lookup(netdev);
348 if (!t) {
349 printk(KERN_DEBUG "fcoe_transport_attach"
350 ":no transport for %s:use %s\n",
351 netdev->name, t->name);
352 return -ENODEV;
353 }
354 /* add to the transport */
355 if (fcoe_transport_device_add(t, netdev)) {
356 printk(KERN_DEBUG "fcoe_transport_attach"
357 ":failed to add %s to tramsport %s\n",
358 netdev->name, t->name);
359 return -EIO;
360 }
361 /* transport create function */
362 if (t->create)
363 t->create(netdev);
364
365 printk(KERN_DEBUG "fcoe_transport_attach:transport %s for %s\n",
366 t->name, netdev->name);
367 return 0;
368}
369EXPORT_SYMBOL_GPL(fcoe_transport_attach);
370
371/**
34f42a07 372 * fcoe_transport_release() - Unload transport from fcoe
85b4aa49
RL
373 * @netdev: the net device on which fcoe is to be released
374 *
375 * Returns: 0 for success
34f42a07 376 */
85b4aa49
RL
377int fcoe_transport_release(struct net_device *netdev)
378{
379 struct fcoe_transport *t;
380
381 /* find the corresponding transport */
382 t = fcoe_transport_lookup(netdev);
383 if (!t) {
384 printk(KERN_DEBUG "fcoe_transport_release:"
385 "no transport for %s:use %s\n",
386 netdev->name, t->name);
387 return -ENODEV;
388 }
389 /* remove the device from the transport */
390 if (fcoe_transport_device_remove(t, netdev)) {
391 printk(KERN_DEBUG "fcoe_transport_release:"
392 "failed to add %s to tramsport %s\n",
393 netdev->name, t->name);
394 return -EIO;
395 }
396 /* transport destroy function */
397 if (t->destroy)
398 t->destroy(netdev);
399
400 printk(KERN_DEBUG "fcoe_transport_release:"
401 "device %s dettached from transport %s\n",
402 netdev->name, t->name);
403
404 return 0;
405}
406EXPORT_SYMBOL_GPL(fcoe_transport_release);
407
408/**
34f42a07 409 * fcoe_transport_init() - Initializes fcoe transport layer
85b4aa49
RL
410 *
411 * This prepares for the fcoe transport layer
412 *
413 * Returns: none
34f42a07 414 */
85b4aa49
RL
415int __init fcoe_transport_init(void)
416{
417 INIT_LIST_HEAD(&fcoe_transports);
418 mutex_init(&fcoe_transports_lock);
419 return 0;
420}
421
422/**
34f42a07
RL
423 * fcoe_transport_exit() - Cleans up the fcoe transport layer
424 *
85b4aa49
RL
425 * This cleans up the fcoe transport layer. removing any transport on the list,
426 * note that the transport destroy func is not called here.
427 *
428 * Returns: none
34f42a07 429 */
85b4aa49
RL
430int __exit fcoe_transport_exit(void)
431{
432 struct fcoe_transport *t, *tmp;
433
434 mutex_lock(&fcoe_transports_lock);
435 list_for_each_entry_safe(t, tmp, &fcoe_transports, list) {
436 list_del(&t->list);
437 mutex_unlock(&fcoe_transports_lock);
438 fcoe_transport_device_remove_all(t);
439 mutex_lock(&fcoe_transports_lock);
440 }
441 mutex_unlock(&fcoe_transports_lock);
442 return 0;
443}