Merge branch 'master' of git://git.infradead.org/users/dedekind/mtd-tests-2.6
[linux-2.6] / drivers / scsi / fcoe / fc_transport_fcoe.c
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 }