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