]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Hannes Reinecke <hare@suse.de> |
2 | Date: Wed, 17 Sep 2008 16:25:23 +0200 | |
3 | Subject: fcoe: Fibre Channel over Ethernet driver | |
4 | References: FATE#303913 | |
5 | ||
6 | Encapsulation protocol for running Fibre Channel over Ethernet interfaces. | |
7 | Creates virtual Fibre Channel host adapters using libfc. | |
8 | ||
9 | Signed-off-by: Robert Love <robert.w.love@intel.com> | |
10 | Signed-off-by: Chris Leech <christopher.leech@intel.com> | |
11 | Signed-off-by: Vasu Dev <vasu.dev@intel.com> | |
12 | Signed-off-by: Yi Zou <yi.zou@intel.com> | |
13 | Signed-off-by: Steve Ma <steve.ma@intel.com> | |
14 | Signed-off-by: Hannes Reinecke <hare@suse.de> | |
15 | --- | |
16 | drivers/scsi/Kconfig | 6 + | |
17 | drivers/scsi/Makefile | 1 + | |
18 | drivers/scsi/fcoe/Makefile | 8 + | |
19 | drivers/scsi/fcoe/fcoe_def.h | 100 +++++++ | |
20 | drivers/scsi/fcoe/fcoe_dev.c | 633 ++++++++++++++++++++++++++++++++++++++++++ | |
21 | drivers/scsi/fcoe/fcoe_if.c | 497 +++++++++++++++++++++++++++++++++ | |
22 | drivers/scsi/fcoe/fcoeinit.c | 440 +++++++++++++++++++++++++++++ | |
23 | 7 files changed, 1685 insertions(+), 0 deletions(-) | |
24 | create mode 100644 drivers/scsi/fcoe/Makefile | |
25 | create mode 100644 drivers/scsi/fcoe/fcoe_def.h | |
26 | create mode 100644 drivers/scsi/fcoe/fcoe_dev.c | |
27 | create mode 100644 drivers/scsi/fcoe/fcoe_if.c | |
28 | create mode 100644 drivers/scsi/fcoe/fcoeinit.c | |
29 | ||
30 | diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig | |
31 | index bd480d2..f382eea 100644 | |
32 | --- a/drivers/scsi/Kconfig | |
33 | +++ b/drivers/scsi/Kconfig | |
34 | @@ -334,6 +334,12 @@ config LIBFC | |
35 | ---help--- | |
36 | Fibre Channel library module | |
37 | ||
38 | +config FCOE | |
39 | + tristate "FCoE module" | |
40 | + depends on LIBFC | |
41 | + ---help--- | |
42 | + Fibre Channel over Ethernet module | |
43 | + | |
44 | config ISCSI_TCP | |
45 | tristate "iSCSI Initiator over TCP/IP" | |
46 | depends on SCSI && INET | |
47 | diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile | |
48 | index 9158dc6..22c01e5 100644 | |
49 | --- a/drivers/scsi/Makefile | |
50 | +++ b/drivers/scsi/Makefile | |
51 | @@ -37,6 +37,7 @@ obj-$(CONFIG_SCSI_SRP_ATTRS) += scsi_transport_srp.o | |
52 | obj-$(CONFIG_SCSI_DH) += device_handler/ | |
53 | ||
54 | obj-$(CONFIG_LIBFC) += libfc/ | |
55 | +obj-$(CONFIG_FCOE) += fcoe/ | |
56 | obj-$(CONFIG_ISCSI_TCP) += libiscsi.o iscsi_tcp.o | |
57 | obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o | |
58 | obj-$(CONFIG_SCSI_A4000T) += 53c700.o a4000t.o | |
59 | diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile | |
60 | new file mode 100644 | |
61 | index 0000000..342e2ad | |
62 | --- /dev/null | |
63 | +++ b/drivers/scsi/fcoe/Makefile | |
64 | @@ -0,0 +1,8 @@ | |
65 | +# $Id: Makefile | |
66 | + | |
67 | +obj-$(CONFIG_FCOE) += fcoe.o | |
68 | + | |
69 | +fcoe-y := \ | |
70 | + fcoe_dev.o \ | |
71 | + fcoe_if.o \ | |
72 | + fcoeinit.o | |
73 | diff --git a/drivers/scsi/fcoe/fcoe_def.h b/drivers/scsi/fcoe/fcoe_def.h | |
74 | new file mode 100644 | |
75 | index 0000000..12bf69c | |
76 | --- /dev/null | |
77 | +++ b/drivers/scsi/fcoe/fcoe_def.h | |
78 | @@ -0,0 +1,100 @@ | |
79 | +/* | |
80 | + * Copyright(c) 2007 Intel Corporation. All rights reserved. | |
81 | + * | |
82 | + * This program is free software; you can redistribute it and/or modify it | |
83 | + * under the terms and conditions of the GNU General Public License, | |
84 | + * version 2, as published by the Free Software Foundation. | |
85 | + * | |
86 | + * This program is distributed in the hope it will be useful, but WITHOUT | |
87 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
88 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
89 | + * more details. | |
90 | + * | |
91 | + * You should have received a copy of the GNU General Public License along with | |
92 | + * this program; if not, write to the Free Software Foundation, Inc., | |
93 | + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | |
94 | + * | |
95 | + * Maintained at www.Open-FCoE.org | |
96 | + */ | |
97 | + | |
98 | +#ifndef _FCOE_DEF_H_ | |
99 | +#define _FCOE_DEF_H_ | |
100 | + | |
101 | +#include <linux/etherdevice.h> | |
102 | +#include <linux/if_ether.h> | |
103 | + | |
104 | +#include <scsi/libfc/libfc.h> | |
105 | + | |
106 | +#include <scsi/fc/fc_fcoe.h> | |
107 | + | |
108 | +#define FCOE_DRIVER_NAME "fcoe" /* driver name for ioctls */ | |
109 | +#define FCOE_DRIVER_VENDOR "Open-FC.org" /* vendor name for ioctls */ | |
110 | + | |
111 | +#define FCOE_MIN_FRAME 36 | |
112 | +#define FCOE_WORD_TO_BYTE 4 | |
113 | + | |
114 | +/* | |
115 | + * this is the main common structure across all instance of fcoe driver. | |
116 | + * There is one to one mapping between hba struct and ethernet nic. | |
117 | + * list of hbas contains pointer to the hba struct, these structures are | |
118 | + * stored in this array using there corresponding if_index. | |
119 | + */ | |
120 | + | |
121 | +struct fcoe_percpu_s { | |
122 | + int cpu; | |
123 | + struct task_struct *thread; | |
124 | + struct sk_buff_head fcoe_rx_list; | |
125 | + struct page *crc_eof_page; | |
126 | + int crc_eof_offset; | |
127 | +}; | |
128 | + | |
129 | +struct fcoe_info { | |
130 | + struct timer_list timer; | |
131 | + /* | |
132 | + * fcoe host list is protected by the following read/write lock | |
133 | + */ | |
134 | + rwlock_t fcoe_hostlist_lock; | |
135 | + struct list_head fcoe_hostlist; | |
136 | + | |
137 | + struct fcoe_percpu_s *fcoe_percpu[NR_CPUS]; | |
138 | +}; | |
139 | + | |
140 | +struct fcoe_softc { | |
141 | + struct list_head list; | |
142 | + struct fc_lport *lp; | |
143 | + struct net_device *real_dev; | |
144 | + struct net_device *phys_dev; /* device with ethtool_ops */ | |
145 | + struct packet_type fcoe_packet_type; | |
146 | + struct sk_buff_head fcoe_pending_queue; | |
147 | + u16 user_mfs; /* configured max frame size */ | |
148 | + | |
149 | + u8 dest_addr[ETH_ALEN]; | |
150 | + u8 ctl_src_addr[ETH_ALEN]; | |
151 | + u8 data_src_addr[ETH_ALEN]; | |
152 | + /* | |
153 | + * fcoe protocol address learning related stuff | |
154 | + */ | |
155 | + u16 flogi_oxid; | |
156 | + u8 flogi_progress; | |
157 | + u8 address_mode; | |
158 | +}; | |
159 | + | |
160 | +extern int debug_fcoe; | |
161 | +extern struct fcoe_percpu_s *fcoe_percpu[]; | |
162 | +extern struct scsi_transport_template *fcoe_transport_template; | |
163 | +int fcoe_percpu_receive_thread(void *arg); | |
164 | + | |
165 | +/* | |
166 | + * HBA transport ops prototypes | |
167 | + */ | |
168 | +extern struct fcoe_info fcoei; | |
169 | + | |
170 | +void fcoe_clean_pending_queue(struct fc_lport *fd); | |
171 | +void fcoe_watchdog(ulong vp); | |
172 | +int fcoe_destroy_interface(const char *ifname); | |
173 | +int fcoe_create_interface(const char *ifname); | |
174 | +int fcoe_xmit(struct fc_lport *, struct fc_frame *); | |
175 | +int fcoe_rcv(struct sk_buff *, struct net_device *, | |
176 | + struct packet_type *, struct net_device *); | |
177 | +int fcoe_link_ok(struct fc_lport *); | |
178 | +#endif /* _FCOE_DEF_H_ */ | |
179 | diff --git a/drivers/scsi/fcoe/fcoe_dev.c b/drivers/scsi/fcoe/fcoe_dev.c | |
180 | new file mode 100644 | |
181 | index 0000000..d5a354f | |
182 | --- /dev/null | |
183 | +++ b/drivers/scsi/fcoe/fcoe_dev.c | |
184 | @@ -0,0 +1,633 @@ | |
185 | +/* | |
186 | + * Copyright(c) 2007 Intel Corporation. All rights reserved. | |
187 | + * | |
188 | + * This program is free software; you can redistribute it and/or modify it | |
189 | + * under the terms and conditions of the GNU General Public License, | |
190 | + * version 2, as published by the Free Software Foundation. | |
191 | + * | |
192 | + * This program is distributed in the hope it will be useful, but WITHOUT | |
193 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
194 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
195 | + * more details. | |
196 | + * | |
197 | + * You should have received a copy of the GNU General Public License along with | |
198 | + * this program; if not, write to the Free Software Foundation, Inc., | |
199 | + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | |
200 | + * | |
201 | + * Maintained at www.Open-FCoE.org | |
202 | + */ | |
203 | + | |
204 | +/* | |
205 | + * FCOE protocol file | |
206 | + */ | |
207 | + | |
208 | +#include <linux/module.h> | |
209 | +#include <linux/version.h> | |
210 | +#include <linux/kernel.h> | |
211 | +#include <linux/spinlock.h> | |
212 | +#include <linux/skbuff.h> | |
213 | +#include <linux/netdevice.h> | |
214 | +#include <linux/etherdevice.h> | |
215 | +#include <linux/if_ether.h> | |
216 | +#include <linux/kthread.h> | |
217 | +#include <linux/crc32.h> | |
218 | +#include <scsi/scsi_tcq.h> | |
219 | +#include <scsi/scsicam.h> | |
220 | +#include <scsi/scsi_transport.h> | |
221 | +#include <scsi/scsi_transport_fc.h> | |
222 | +#include <net/rtnetlink.h> | |
223 | + | |
224 | +#include <scsi/fc/fc_encaps.h> | |
225 | + | |
226 | +#include <scsi/libfc/libfc.h> | |
227 | +#include <scsi/libfc/fc_frame.h> | |
228 | + | |
229 | +#include <scsi/fc/fc_fcoe.h> | |
230 | +#include "fcoe_def.h" | |
231 | + | |
232 | +#define FCOE_MAX_QUEUE_DEPTH 256 | |
233 | + | |
234 | +/* destination address mode */ | |
235 | +#define FCOE_GW_ADDR_MODE 0x00 | |
236 | +#define FCOE_FCOUI_ADDR_MODE 0x01 | |
237 | + | |
238 | +/* Function Prototyes */ | |
239 | +static int fcoe_check_wait_queue(struct fc_lport *); | |
240 | +static void fcoe_insert_wait_queue_head(struct fc_lport *, struct sk_buff *); | |
241 | +static void fcoe_insert_wait_queue(struct fc_lport *, struct sk_buff *); | |
242 | +static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *); | |
243 | + | |
244 | +/* | |
245 | + * this is the fcoe receive function | |
246 | + * called by NET_RX_SOFTIRQ | |
247 | + * this function will receive the packet and | |
248 | + * build fc frame and pass it up | |
249 | + */ | |
250 | +int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, | |
251 | + struct packet_type *ptype, struct net_device *olddev) | |
252 | +{ | |
253 | + struct fc_lport *lp; | |
254 | + struct fcoe_rcv_info *fr; | |
255 | + struct fcoe_softc *fc; | |
256 | + struct fcoe_dev_stats *stats; | |
257 | + u8 *data; | |
258 | + struct fc_frame_header *fh; | |
259 | + unsigned short oxid; | |
260 | + int cpu_idx; | |
261 | + struct fcoe_percpu_s *fps; | |
262 | + struct fcoe_info *fci = &fcoei; | |
263 | + | |
264 | + fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type); | |
265 | + lp = fc->lp; | |
266 | + if (unlikely(lp == NULL)) { | |
267 | + FC_DBG("cannot find hba structure"); | |
268 | + goto err2; | |
269 | + } | |
270 | + | |
271 | + if (unlikely(debug_fcoe)) { | |
272 | + FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p " | |
273 | + "end:%p sum:%d dev:%s", skb->len, skb->data_len, | |
274 | + skb->head, skb->data, skb_tail_pointer(skb), | |
275 | + skb_end_pointer(skb), skb->csum, | |
276 | + skb->dev ? skb->dev->name : "<NULL>"); | |
277 | + | |
278 | + } | |
279 | + | |
280 | + /* check for FCOE packet type */ | |
281 | + if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) { | |
282 | + FC_DBG("wrong FC type frame"); | |
283 | + goto err; | |
284 | + } | |
285 | + data = skb->data; | |
286 | + data += sizeof(struct fcoe_hdr); | |
287 | + fh = (struct fc_frame_header *)data; | |
288 | + oxid = ntohs(fh->fh_ox_id); | |
289 | + | |
290 | + fr = fcoe_dev_from_skb(skb); | |
291 | + fr->fr_dev = lp; | |
292 | + fr->ptype = ptype; | |
293 | + cpu_idx = 0; | |
294 | +#ifdef CONFIG_SMP | |
295 | + /* | |
296 | + * The exchange ID are ANDed with num of online CPUs, | |
297 | + * so that will have the least lock contention in | |
298 | + * handling the exchange. if there is no thread | |
299 | + * for a given idx then use first online cpu. | |
300 | + */ | |
301 | + cpu_idx = oxid & (num_online_cpus() >> 1); | |
302 | + if (fci->fcoe_percpu[cpu_idx] == NULL) | |
303 | + cpu_idx = first_cpu(cpu_online_map); | |
304 | +#endif | |
305 | + fps = fci->fcoe_percpu[cpu_idx]; | |
306 | + | |
307 | + spin_lock_bh(&fps->fcoe_rx_list.lock); | |
308 | + __skb_queue_tail(&fps->fcoe_rx_list, skb); | |
309 | + if (fps->fcoe_rx_list.qlen == 1) | |
310 | + wake_up_process(fps->thread); | |
311 | + | |
312 | + spin_unlock_bh(&fps->fcoe_rx_list.lock); | |
313 | + | |
314 | + return 0; | |
315 | +err: | |
316 | +#ifdef CONFIG_SMP | |
317 | + stats = lp->dev_stats[smp_processor_id()]; | |
318 | +#else | |
319 | + stats = lp->dev_stats[0]; | |
320 | +#endif | |
321 | + stats->ErrorFrames++; | |
322 | + | |
323 | +err2: | |
324 | + kfree_skb(skb); | |
325 | + return -1; | |
326 | +} | |
327 | + | |
328 | +static inline int fcoe_start_io(struct sk_buff *skb) | |
329 | +{ | |
330 | + int rc; | |
331 | + | |
332 | + skb_get(skb); | |
333 | + rc = dev_queue_xmit(skb); | |
334 | + if (rc != 0) | |
335 | + return rc; | |
336 | + kfree_skb(skb); | |
337 | + return 0; | |
338 | +} | |
339 | + | |
340 | +static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen) | |
341 | +{ | |
342 | + struct fcoe_info *fci = &fcoei; | |
343 | + struct fcoe_percpu_s *fps; | |
344 | + struct page *page; | |
345 | + int cpu_idx; | |
346 | + | |
347 | + cpu_idx = get_cpu(); | |
348 | + fps = fci->fcoe_percpu[cpu_idx]; | |
349 | + page = fps->crc_eof_page; | |
350 | + if (!page) { | |
351 | + page = alloc_page(GFP_ATOMIC); | |
352 | + if (!page) { | |
353 | + put_cpu(); | |
354 | + return -ENOMEM; | |
355 | + } | |
356 | + fps->crc_eof_page = page; | |
357 | + WARN_ON(fps->crc_eof_offset != 0); | |
358 | + } | |
359 | + | |
360 | + get_page(page); | |
361 | + skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, | |
362 | + fps->crc_eof_offset, tlen); | |
363 | + skb->len += tlen; | |
364 | + skb->data_len += tlen; | |
365 | + skb->truesize += tlen; | |
366 | + fps->crc_eof_offset += sizeof(struct fcoe_crc_eof); | |
367 | + | |
368 | + if (fps->crc_eof_offset >= PAGE_SIZE) { | |
369 | + fps->crc_eof_page = NULL; | |
370 | + fps->crc_eof_offset = 0; | |
371 | + put_page(page); | |
372 | + } | |
373 | + put_cpu(); | |
374 | + return 0; | |
375 | +} | |
376 | + | |
377 | +/* | |
378 | + * this is the frame xmit routine | |
379 | + */ | |
380 | +int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) | |
381 | +{ | |
382 | + int indx; | |
383 | + int wlen, rc = 0; | |
384 | + u32 crc; | |
385 | + struct ethhdr *eh; | |
386 | + struct fcoe_crc_eof *cp; | |
387 | + struct sk_buff *skb; | |
388 | + struct fcoe_dev_stats *stats; | |
389 | + struct fc_frame_header *fh; | |
390 | + unsigned int hlen; /* header length implies the version */ | |
391 | + unsigned int tlen; /* trailer length */ | |
392 | + int flogi_in_progress = 0; | |
393 | + struct fcoe_softc *fc; | |
394 | + void *data; | |
395 | + u8 sof, eof; | |
396 | + struct fcoe_hdr *hp; | |
397 | + | |
398 | + WARN_ON((fr_len(fp) % sizeof(u32)) != 0); | |
399 | + | |
400 | + fc = (struct fcoe_softc *)lp->drv_priv; | |
401 | + /* | |
402 | + * if it is a flogi then we need to learn gw-addr | |
403 | + * and my own fcid | |
404 | + */ | |
405 | + fh = fc_frame_header_get(fp); | |
406 | + if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) { | |
407 | + if (fc_frame_payload_op(fp) == ELS_FLOGI) { | |
408 | + fc->flogi_oxid = ntohs(fh->fh_ox_id); | |
409 | + fc->address_mode = FCOE_FCOUI_ADDR_MODE; | |
410 | + fc->flogi_progress = 1; | |
411 | + flogi_in_progress = 1; | |
412 | + } else if (fc->flogi_progress && ntoh24(fh->fh_s_id) != 0) { | |
413 | + /* | |
414 | + * Here we must've gotten an SID by accepting an FLOGI | |
415 | + * from a point-to-point connection. Switch to using | |
416 | + * the source mac based on the SID. The destination | |
417 | + * MAC in this case would have been set by receving the | |
418 | + * FLOGI. | |
419 | + */ | |
420 | + fc_fcoe_set_mac(fc->data_src_addr, fh->fh_s_id); | |
421 | + fc->flogi_progress = 0; | |
422 | + } | |
423 | + } | |
424 | + | |
425 | + skb = fp_skb(fp); | |
426 | + sof = fr_sof(fp); | |
427 | + eof = fr_eof(fp); | |
428 | + | |
429 | + crc = ~0; | |
430 | + crc = crc32(crc, skb->data, skb_headlen(skb)); | |
431 | + | |
432 | + for (indx = 0; indx < skb_shinfo(skb)->nr_frags; indx++) { | |
433 | + skb_frag_t *frag = &skb_shinfo(skb)->frags[indx]; | |
434 | + unsigned long off = frag->page_offset; | |
435 | + unsigned long len = frag->size; | |
436 | + | |
437 | + while (len > 0) { | |
438 | + unsigned long clen; | |
439 | + | |
440 | + clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK)); | |
441 | + data = kmap_atomic(frag->page + (off >> PAGE_SHIFT), | |
442 | + KM_SKB_DATA_SOFTIRQ); | |
443 | + crc = crc32(crc, data + (off & ~PAGE_MASK), | |
444 | + clen); | |
445 | + kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ); | |
446 | + off += clen; | |
447 | + len -= clen; | |
448 | + } | |
449 | + } | |
450 | + | |
451 | + /* | |
452 | + * Get header and trailer lengths. | |
453 | + * This is temporary code until we get rid of the old protocol. | |
454 | + * Both versions have essentially the same trailer layout but T11 | |
455 | + * has padding afterwards. | |
456 | + */ | |
457 | + hlen = sizeof(struct fcoe_hdr); | |
458 | + tlen = sizeof(struct fcoe_crc_eof); | |
459 | + | |
460 | + /* | |
461 | + * copy fc crc and eof to the skb buff | |
462 | + * Use utility buffer in the fc_frame part of the sk_buff for the | |
463 | + * trailer. | |
464 | + * We don't do a get_page for this frag, since that page may not be | |
465 | + * managed that way. So that skb_free() doesn't do that either, we | |
466 | + * setup the destructor to remove this frag. | |
467 | + */ | |
468 | + if (skb_is_nonlinear(skb)) { | |
469 | + skb_frag_t *frag; | |
470 | + if (fcoe_get_paged_crc_eof(skb, tlen)) { | |
471 | + kfree(skb); | |
472 | + return -ENOMEM; | |
473 | + } | |
474 | + frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1]; | |
475 | + cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ) | |
476 | + + frag->page_offset; | |
477 | + } else { | |
478 | + cp = (struct fcoe_crc_eof *)skb_put(skb, tlen); | |
479 | + } | |
480 | + | |
481 | + cp->fcoe_eof = eof; | |
482 | + cp->fcoe_crc32 = cpu_to_le32(~crc); | |
483 | + if (tlen == sizeof(*cp)) | |
484 | + memset(cp->fcoe_resvd, 0, sizeof(cp->fcoe_resvd)); | |
485 | + wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE; | |
486 | + | |
487 | + if (skb_is_nonlinear(skb)) { | |
488 | + kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ); | |
489 | + cp = NULL; | |
490 | + } | |
491 | + | |
492 | + /* | |
493 | + * Fill in the control structures | |
494 | + */ | |
495 | + skb->ip_summed = CHECKSUM_NONE; | |
496 | + eh = (struct ethhdr *)skb_push(skb, hlen + sizeof(struct ethhdr)); | |
497 | + if (fc->address_mode == FCOE_FCOUI_ADDR_MODE) | |
498 | + fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id); | |
499 | + else | |
500 | + /* insert GW address */ | |
501 | + memcpy(eh->h_dest, fc->dest_addr, ETH_ALEN); | |
502 | + | |
503 | + if (unlikely(flogi_in_progress)) | |
504 | + memcpy(eh->h_source, fc->ctl_src_addr, ETH_ALEN); | |
505 | + else | |
506 | + memcpy(eh->h_source, fc->data_src_addr, ETH_ALEN); | |
507 | + | |
508 | + eh->h_proto = htons(ETH_P_FCOE); | |
509 | + skb->protocol = htons(ETH_P_802_3); | |
510 | + skb_reset_mac_header(skb); | |
511 | + skb_reset_network_header(skb); | |
512 | + | |
513 | + hp = (struct fcoe_hdr *)(eh + 1); | |
514 | + memset(hp, 0, sizeof(*hp)); | |
515 | + if (FC_FCOE_VER) | |
516 | + FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER); | |
517 | + hp->fcoe_sof = sof; | |
518 | + | |
519 | + stats = lp->dev_stats[smp_processor_id()]; | |
520 | + stats->TxFrames++; | |
521 | + stats->TxWords += wlen; | |
522 | + skb->dev = fc->real_dev; | |
523 | + | |
524 | + fr_dev(fp) = lp; | |
525 | + if (fc->fcoe_pending_queue.qlen) | |
526 | + rc = fcoe_check_wait_queue(lp); | |
527 | + | |
528 | + if (rc == 0) | |
529 | + rc = fcoe_start_io(skb); | |
530 | + | |
531 | + if (rc) { | |
532 | + fcoe_insert_wait_queue(lp, skb); | |
533 | + if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) | |
534 | + fc_pause(lp); | |
535 | + } | |
536 | + | |
537 | + return 0; | |
538 | +} | |
539 | + | |
540 | +int fcoe_percpu_receive_thread(void *arg) | |
541 | +{ | |
542 | + struct fcoe_percpu_s *p = arg; | |
543 | + u32 fr_len; | |
544 | + unsigned int hlen; | |
545 | + unsigned int tlen; | |
546 | + struct fc_lport *lp; | |
547 | + struct fcoe_rcv_info *fr; | |
548 | + struct fcoe_dev_stats *stats; | |
549 | + struct fc_frame_header *fh; | |
550 | + struct sk_buff *skb; | |
551 | + struct fcoe_crc_eof *cp; | |
552 | + enum fc_sof sof; | |
553 | + struct fc_frame *fp; | |
554 | + u8 *mac = NULL; | |
555 | + struct fcoe_softc *fc; | |
556 | + struct fcoe_hdr *hp; | |
557 | + | |
558 | + set_user_nice(current, 19); | |
559 | + | |
560 | + while (!kthread_should_stop()) { | |
561 | + | |
562 | + spin_lock_bh(&p->fcoe_rx_list.lock); | |
563 | + while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) { | |
564 | + set_current_state(TASK_INTERRUPTIBLE); | |
565 | + spin_unlock_bh(&p->fcoe_rx_list.lock); | |
566 | + schedule(); | |
567 | + set_current_state(TASK_RUNNING); | |
568 | + if (kthread_should_stop()) | |
569 | + return 0; | |
570 | + spin_lock_bh(&p->fcoe_rx_list.lock); | |
571 | + } | |
572 | + spin_unlock_bh(&p->fcoe_rx_list.lock); | |
573 | + fr = fcoe_dev_from_skb(skb); | |
574 | + lp = fr->fr_dev; | |
575 | + if (unlikely(lp == NULL)) { | |
576 | + FC_DBG("invalid HBA Structure"); | |
577 | + kfree_skb(skb); | |
578 | + continue; | |
579 | + } | |
580 | + | |
581 | + stats = lp->dev_stats[smp_processor_id()]; | |
582 | + | |
583 | + if (unlikely(debug_fcoe)) { | |
584 | + FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p " | |
585 | + "tail:%p end:%p sum:%d dev:%s", | |
586 | + skb->len, skb->data_len, | |
587 | + skb->head, skb->data, skb_tail_pointer(skb), | |
588 | + skb_end_pointer(skb), skb->csum, | |
589 | + skb->dev ? skb->dev->name : "<NULL>"); | |
590 | + } | |
591 | + | |
592 | + /* | |
593 | + * Save source MAC address before discarding header. | |
594 | + */ | |
595 | + fc = lp->drv_priv; | |
596 | + if (unlikely(fc->flogi_progress)) | |
597 | + mac = eth_hdr(skb)->h_source; | |
598 | + | |
599 | + if (skb_is_nonlinear(skb)) | |
600 | + skb_linearize(skb); /* not ideal */ | |
601 | + | |
602 | + /* | |
603 | + * Check the header and pull it off. | |
604 | + */ | |
605 | + hlen = sizeof(struct fcoe_hdr); | |
606 | + | |
607 | + hp = (struct fcoe_hdr *)skb->data; | |
608 | + if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { | |
609 | + if (stats->ErrorFrames < 5) | |
610 | + FC_DBG("unknown FCoE version %x", | |
611 | + FC_FCOE_DECAPS_VER(hp)); | |
612 | + stats->ErrorFrames++; | |
613 | + kfree_skb(skb); | |
614 | + continue; | |
615 | + } | |
616 | + sof = hp->fcoe_sof; | |
617 | + skb_pull(skb, sizeof(*hp)); | |
618 | + fr_len = skb->len - sizeof(struct fcoe_crc_eof); | |
619 | + skb_trim(skb, fr_len); | |
620 | + tlen = sizeof(struct fcoe_crc_eof); | |
621 | + | |
622 | + if (unlikely(fr_len > skb->len)) { | |
623 | + if (stats->ErrorFrames < 5) | |
624 | + FC_DBG("length error fr_len 0x%x skb->len 0x%x", | |
625 | + fr_len, skb->len); | |
626 | + stats->ErrorFrames++; | |
627 | + kfree_skb(skb); | |
628 | + continue; | |
629 | + } | |
630 | + stats->RxFrames++; | |
631 | + stats->RxWords += fr_len / FCOE_WORD_TO_BYTE; | |
632 | + | |
633 | + fp = (struct fc_frame *) skb; | |
634 | + fc_frame_init(fp); | |
635 | + cp = (struct fcoe_crc_eof *)(skb->data + fr_len); | |
636 | + fr_eof(fp) = cp->fcoe_eof; | |
637 | + fr_sof(fp) = sof; | |
638 | + fr_dev(fp) = lp; | |
639 | + | |
640 | + /* | |
641 | + * Check the CRC here, unless it's solicited data for SCSI. | |
642 | + * In that case, the SCSI layer can check it during the copy, | |
643 | + * and it'll be more cache-efficient. | |
644 | + */ | |
645 | + fh = fc_frame_header_get(fp); | |
646 | + if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && | |
647 | + fh->fh_type == FC_TYPE_FCP) { | |
648 | + fr_flags(fp) |= FCPHF_CRC_UNCHECKED; | |
649 | + fc_exch_recv(lp, lp->emp, fp); | |
650 | + } else if (le32_to_cpu(cp->fcoe_crc32) == | |
651 | + ~crc32(~0, skb->data, fr_len)) { | |
652 | + if (unlikely(fc->flogi_progress)) | |
653 | + fcoe_recv_flogi(fc, fp, mac); | |
654 | + fc_exch_recv(lp, lp->emp, fp); | |
655 | + } else { | |
656 | + if (debug_fcoe || stats->InvalidCRCCount < 5) { | |
657 | + printk(KERN_WARNING \ | |
658 | + "fcoe: dropping frame with CRC error"); | |
659 | + } | |
660 | + stats->InvalidCRCCount++; | |
661 | + stats->ErrorFrames++; | |
662 | + fc_frame_free(fp); | |
663 | + } | |
664 | + } | |
665 | + return 0; | |
666 | +} | |
667 | + | |
668 | +/* | |
669 | + * Snoop potential response to FLOGI or even incoming FLOGI. | |
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 | +void fcoe_watchdog(ulong vp) | |
718 | +{ | |
719 | + struct fc_lport *lp; | |
720 | + struct fcoe_softc *fc; | |
721 | + struct fcoe_info *fci = &fcoei; | |
722 | + int paused = 0; | |
723 | + | |
724 | + read_lock(&fci->fcoe_hostlist_lock); | |
725 | + list_for_each_entry(fc, &fci->fcoe_hostlist, list) { | |
726 | + lp = fc->lp; | |
727 | + if (lp) { | |
728 | + if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) | |
729 | + paused = 1; | |
730 | + if (fcoe_check_wait_queue(lp) < FCOE_MAX_QUEUE_DEPTH) { | |
731 | + if (paused) | |
732 | + fc_unpause(lp); | |
733 | + } | |
734 | + } | |
735 | + } | |
736 | + read_unlock(&fci->fcoe_hostlist_lock); | |
737 | + | |
738 | + fci->timer.expires = jiffies + (1 * HZ); | |
739 | + add_timer(&fci->timer); | |
740 | +} | |
741 | + | |
742 | +/* | |
743 | + * the wait_queue is used when the skb transmit fails. skb will go | |
744 | + * in the wait_queue which will be emptied by the time function OR | |
745 | + * by the next skb transmit. | |
746 | + * | |
747 | + */ | |
748 | + | |
749 | +/* | |
750 | + * Function name : fcoe_check_wait_queue() | |
751 | + * | |
752 | + * Return Values : 0 or error | |
753 | + * | |
754 | + * Description : empties the wait_queue | |
755 | + * dequeue the head of the wait_queue queue and | |
756 | + * calls fcoe_start_io() for each packet | |
757 | + * if all skb have been transmitted, return 0 | |
758 | + * if a error occurs, then restore wait_queue and try again | |
759 | + * later | |
760 | + * | |
761 | + */ | |
762 | + | |
763 | +static int fcoe_check_wait_queue(struct fc_lport *lp) | |
764 | +{ | |
765 | + int rc, unpause = 0; | |
766 | + int paused = 0; | |
767 | + struct sk_buff *skb; | |
768 | + struct fcoe_softc *fc; | |
769 | + | |
770 | + fc = (struct fcoe_softc *)lp->drv_priv; | |
771 | + spin_lock_bh(&fc->fcoe_pending_queue.lock); | |
772 | + | |
773 | + /* | |
774 | + * is this interface paused? | |
775 | + */ | |
776 | + if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) | |
777 | + paused = 1; | |
778 | + if (fc->fcoe_pending_queue.qlen) { | |
779 | + while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) { | |
780 | + spin_unlock_bh(&fc->fcoe_pending_queue.lock); | |
781 | + rc = fcoe_start_io(skb); | |
782 | + if (rc) { | |
783 | + fcoe_insert_wait_queue_head(lp, skb); | |
784 | + return rc; | |
785 | + } | |
786 | + spin_lock_bh(&fc->fcoe_pending_queue.lock); | |
787 | + } | |
788 | + if (fc->fcoe_pending_queue.qlen < FCOE_MAX_QUEUE_DEPTH) | |
789 | + unpause = 1; | |
790 | + } | |
791 | + spin_unlock_bh(&fc->fcoe_pending_queue.lock); | |
792 | + if ((unpause) && (paused)) | |
793 | + fc_unpause(lp); | |
794 | + return fc->fcoe_pending_queue.qlen; | |
795 | +} | |
796 | + | |
797 | +static void fcoe_insert_wait_queue_head(struct fc_lport *lp, | |
798 | + struct sk_buff *skb) | |
799 | +{ | |
800 | + struct fcoe_softc *fc; | |
801 | + | |
802 | + fc = (struct fcoe_softc *)lp->drv_priv; | |
803 | + spin_lock_bh(&fc->fcoe_pending_queue.lock); | |
804 | + __skb_queue_head(&fc->fcoe_pending_queue, skb); | |
805 | + spin_unlock_bh(&fc->fcoe_pending_queue.lock); | |
806 | +} | |
807 | + | |
808 | +static void fcoe_insert_wait_queue(struct fc_lport *lp, | |
809 | + struct sk_buff *skb) | |
810 | +{ | |
811 | + struct fcoe_softc *fc; | |
812 | + | |
813 | + fc = (struct fcoe_softc *)lp->drv_priv; | |
814 | + spin_lock_bh(&fc->fcoe_pending_queue.lock); | |
815 | + __skb_queue_tail(&fc->fcoe_pending_queue, skb); | |
816 | + spin_unlock_bh(&fc->fcoe_pending_queue.lock); | |
817 | +} | |
818 | diff --git a/drivers/scsi/fcoe/fcoe_if.c b/drivers/scsi/fcoe/fcoe_if.c | |
819 | new file mode 100644 | |
820 | index 0000000..7f983e2 | |
821 | --- /dev/null | |
822 | +++ b/drivers/scsi/fcoe/fcoe_if.c | |
823 | @@ -0,0 +1,497 @@ | |
824 | +/* | |
825 | + * Copyright(c) 2007 Intel Corporation. All rights reserved. | |
826 | + * | |
827 | + * This program is free software; you can redistribute it and/or modify it | |
828 | + * under the terms and conditions of the GNU General Public License, | |
829 | + * version 2, as published by the Free Software Foundation. | |
830 | + * | |
831 | + * This program is distributed in the hope it will be useful, but WITHOUT | |
832 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
833 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
834 | + * more details. | |
835 | + * | |
836 | + * You should have received a copy of the GNU General Public License along with | |
837 | + * this program; if not, write to the Free Software Foundation, Inc., | |
838 | + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | |
839 | + * | |
840 | + * Maintained at www.Open-FCoE.org | |
841 | + */ | |
842 | + | |
843 | +/* | |
844 | + * FCOE protocol file | |
845 | + */ | |
846 | + | |
847 | +#include <linux/module.h> | |
848 | +#include <linux/version.h> | |
849 | +#include <linux/kernel.h> | |
850 | +#include <linux/init.h> | |
851 | +#include <linux/spinlock.h> | |
852 | +#include <linux/netdevice.h> | |
853 | +#include <linux/etherdevice.h> | |
854 | +#include <linux/ethtool.h> | |
855 | +#include <linux/if_ether.h> | |
856 | +#include <linux/if_vlan.h> | |
857 | +#include <net/rtnetlink.h> | |
858 | + | |
859 | +#include <scsi/fc/fc_els.h> | |
860 | +#include <scsi/fc/fc_encaps.h> | |
861 | +#include <scsi/fc/fc_fs.h> | |
862 | +#include <scsi/scsi_transport.h> | |
863 | +#include <scsi/scsi_transport_fc.h> | |
864 | + | |
865 | +#include <scsi/libfc/libfc.h> | |
866 | + | |
867 | +#include <scsi/fc/fc_fcoe.h> | |
868 | +#include "fcoe_def.h" | |
869 | + | |
870 | +#define FCOE_VERSION "0.1" | |
871 | + | |
872 | +#define FCOE_MAX_LUN 255 | |
873 | +#define FCOE_MAX_FCP_TARGET 256 | |
874 | + | |
875 | +#define FCOE_MIN_XID 0x0004 | |
876 | +#define FCOE_MAX_XID 0x07ef | |
877 | + | |
878 | +int debug_fcoe; | |
879 | + | |
880 | +struct fcoe_info fcoei = { | |
881 | + .fcoe_hostlist = LIST_HEAD_INIT(fcoei.fcoe_hostlist), | |
882 | +}; | |
883 | + | |
884 | +static struct fcoe_softc *fcoe_find_fc_lport(const char *name) | |
885 | +{ | |
886 | + struct fcoe_softc *fc; | |
887 | + struct fc_lport *lp; | |
888 | + struct fcoe_info *fci = &fcoei; | |
889 | + | |
890 | + read_lock(&fci->fcoe_hostlist_lock); | |
891 | + list_for_each_entry(fc, &fci->fcoe_hostlist, list) { | |
892 | + lp = fc->lp; | |
893 | + if (!strncmp(name, lp->ifname, IFNAMSIZ)) { | |
894 | + read_unlock(&fci->fcoe_hostlist_lock); | |
895 | + return fc; | |
896 | + } | |
897 | + } | |
898 | + read_unlock(&fci->fcoe_hostlist_lock); | |
899 | + return NULL; | |
900 | +} | |
901 | + | |
902 | +/* | |
903 | + * Convert 48-bit IEEE MAC address to 64-bit FC WWN. | |
904 | + */ | |
905 | +static u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN], | |
906 | + unsigned int scheme, unsigned int port) | |
907 | +{ | |
908 | + u64 wwn; | |
909 | + u64 host_mac; | |
910 | + | |
911 | + /* The MAC is in NO, so flip only the low 48 bits */ | |
912 | + host_mac = ((u64) mac[0] << 40) | | |
913 | + ((u64) mac[1] << 32) | | |
914 | + ((u64) mac[2] << 24) | | |
915 | + ((u64) mac[3] << 16) | | |
916 | + ((u64) mac[4] << 8) | | |
917 | + (u64) mac[5]; | |
918 | + | |
919 | + WARN_ON(host_mac >= (1ULL << 48)); | |
920 | + wwn = host_mac | ((u64) scheme << 60); | |
921 | + switch (scheme) { | |
922 | + case 1: | |
923 | + WARN_ON(port != 0); | |
924 | + break; | |
925 | + case 2: | |
926 | + WARN_ON(port >= 0xfff); | |
927 | + wwn |= (u64) port << 48; | |
928 | + break; | |
929 | + default: | |
930 | + WARN_ON(1); | |
931 | + break; | |
932 | + } | |
933 | + | |
934 | + return wwn; | |
935 | +} | |
936 | + | |
937 | +static struct scsi_host_template fcoe_driver_template = { | |
938 | + .module = THIS_MODULE, | |
939 | + .name = "FCoE Driver", | |
940 | + .proc_name = FCOE_DRIVER_NAME, | |
941 | + .queuecommand = fc_queuecommand, | |
942 | + .eh_abort_handler = fc_eh_abort, | |
943 | + .eh_device_reset_handler = fc_eh_device_reset, | |
944 | + .eh_host_reset_handler = fc_eh_host_reset, | |
945 | + .slave_alloc = fc_slave_alloc, | |
946 | + .change_queue_depth = fc_change_queue_depth, | |
947 | + .change_queue_type = fc_change_queue_type, | |
948 | + .this_id = -1, | |
949 | + .cmd_per_lun = 32, | |
950 | + .can_queue = FC_MAX_OUTSTANDING_COMMANDS, | |
951 | + .use_clustering = ENABLE_CLUSTERING, | |
952 | + .sg_tablesize = 4, | |
953 | + .max_sectors = 0xffff, | |
954 | +}; | |
955 | + | |
956 | +int fcoe_destroy_interface(const char *ifname) | |
957 | +{ | |
958 | + int cpu, idx; | |
959 | + struct fcoe_dev_stats *p; | |
960 | + struct fcoe_percpu_s *pp; | |
961 | + struct fcoe_softc *fc; | |
962 | + struct fcoe_rcv_info *fr; | |
963 | + struct fcoe_info *fci = &fcoei; | |
964 | + struct sk_buff_head *list; | |
965 | + struct sk_buff *skb, *next; | |
966 | + struct sk_buff *head; | |
967 | + struct fc_lport *lp; | |
968 | + u8 flogi_maddr[ETH_ALEN]; | |
969 | + | |
970 | + fc = fcoe_find_fc_lport(ifname); | |
971 | + if (!fc) | |
972 | + return -ENODEV; | |
973 | + | |
974 | + lp = fc->lp; | |
975 | + | |
976 | + /* Remove the instance from fcoe's list */ | |
977 | + write_lock_bh(&fci->fcoe_hostlist_lock); | |
978 | + list_del(&fc->list); | |
979 | + write_unlock_bh(&fci->fcoe_hostlist_lock); | |
980 | + | |
981 | + /* Cleanup the fc_lport */ | |
982 | + fc_lport_destroy(lp); | |
983 | + fc_fcp_destroy(lp); | |
984 | + if (lp->emp) | |
985 | + fc_exch_mgr_free(lp->emp); | |
986 | + | |
987 | + /* Detach from the scsi-ml */ | |
988 | + fc_remove_host(lp->host); | |
989 | + scsi_remove_host(lp->host); | |
990 | + | |
991 | + /* Don't listen for Ethernet packets anymore */ | |
992 | + dev_remove_pack(&fc->fcoe_packet_type); | |
993 | + | |
994 | + /* Delete secondary MAC addresses */ | |
995 | + rtnl_lock(); | |
996 | + memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); | |
997 | + dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN); | |
998 | + if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 })) | |
999 | + dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN); | |
1000 | + rtnl_unlock(); | |
1001 | + | |
1002 | + /* Free the per-CPU revieve threads */ | |
1003 | + for (idx = 0; idx < NR_CPUS; idx++) { | |
1004 | + if (fci->fcoe_percpu[idx]) { | |
1005 | + pp = fci->fcoe_percpu[idx]; | |
1006 | + spin_lock_bh(&pp->fcoe_rx_list.lock); | |
1007 | + list = &pp->fcoe_rx_list; | |
1008 | + head = list->next; | |
1009 | + for (skb = head; skb != (struct sk_buff *)list; | |
1010 | + skb = next) { | |
1011 | + next = skb->next; | |
1012 | + fr = fcoe_dev_from_skb(skb); | |
1013 | + if (fr->fr_dev == fc->lp) { | |
1014 | + __skb_unlink(skb, list); | |
1015 | + kfree_skb(skb); | |
1016 | + } | |
1017 | + } | |
1018 | + spin_unlock_bh(&pp->fcoe_rx_list.lock); | |
1019 | + } | |
1020 | + } | |
1021 | + | |
1022 | + /* Free existing skbs */ | |
1023 | + fcoe_clean_pending_queue(lp); | |
1024 | + | |
1025 | + /* Free memory used by statistical counters */ | |
1026 | + for_each_online_cpu(cpu) { | |
1027 | + p = lp->dev_stats[cpu]; | |
1028 | + if (p) { | |
1029 | + lp->dev_stats[cpu] = NULL; | |
1030 | + kfree(p); | |
1031 | + } | |
1032 | + } | |
1033 | + | |
1034 | + /* Release the net_device and Scsi_Host */ | |
1035 | + dev_put(fc->real_dev); | |
1036 | + scsi_host_put(lp->host); | |
1037 | + return 0; | |
1038 | +} | |
1039 | + | |
1040 | +/* | |
1041 | + * Return zero if link is OK for use by FCoE. | |
1042 | + * Any permanently-disqualifying conditions have been previously checked. | |
1043 | + * This also updates the speed setting, which may change with link for 100/1000. | |
1044 | + * | |
1045 | + * This function should probably be checking for PAUSE support at some point | |
1046 | + * in the future. Currently Per-priority-pause is not determinable using | |
1047 | + * ethtool, so we shouldn't be restrictive until that problem is resolved. | |
1048 | + */ | |
1049 | +int fcoe_link_ok(struct fc_lport *lp) | |
1050 | +{ | |
1051 | + struct fcoe_softc *fc = (struct fcoe_softc *)lp->drv_priv; | |
1052 | + struct net_device *dev = fc->real_dev; | |
1053 | + struct ethtool_cmd ecmd = { ETHTOOL_GSET }; | |
1054 | + int rc = 0; | |
1055 | + | |
1056 | + if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) { | |
1057 | + dev = fc->phys_dev; | |
1058 | + if (dev->ethtool_ops->get_settings) { | |
1059 | + dev->ethtool_ops->get_settings(dev, &ecmd); | |
1060 | + lp->link_supported_speeds &= | |
1061 | + ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); | |
1062 | + if (ecmd.supported & (SUPPORTED_1000baseT_Half | | |
1063 | + SUPPORTED_1000baseT_Full)) | |
1064 | + lp->link_supported_speeds |= FC_PORTSPEED_1GBIT; | |
1065 | + if (ecmd.supported & SUPPORTED_10000baseT_Full) | |
1066 | + lp->link_supported_speeds |= | |
1067 | + FC_PORTSPEED_10GBIT; | |
1068 | + if (ecmd.speed == SPEED_1000) | |
1069 | + lp->link_speed = FC_PORTSPEED_1GBIT; | |
1070 | + if (ecmd.speed == SPEED_10000) | |
1071 | + lp->link_speed = FC_PORTSPEED_10GBIT; | |
1072 | + } | |
1073 | + } else | |
1074 | + rc = -1; | |
1075 | + | |
1076 | + return rc; | |
1077 | +} | |
1078 | + | |
1079 | +static struct libfc_function_template fcoe_libfc_fcn_templ = { | |
1080 | + .frame_send = fcoe_xmit, | |
1081 | +}; | |
1082 | + | |
1083 | +static int lport_config(struct fc_lport *lp, struct Scsi_Host *shost) | |
1084 | +{ | |
1085 | + int i = 0; | |
1086 | + struct fcoe_dev_stats *p; | |
1087 | + | |
1088 | + lp->host = shost; | |
1089 | + lp->drv_priv = (void *)(lp + 1); | |
1090 | + | |
1091 | + lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3, | |
1092 | + FCOE_MIN_XID, FCOE_MAX_XID); | |
1093 | + if (!lp->emp) | |
1094 | + return -ENOMEM; | |
1095 | + | |
1096 | + lp->link_status = 0; | |
1097 | + lp->max_retry_count = 3; | |
1098 | + lp->e_d_tov = 2 * 1000; /* FC-FS default */ | |
1099 | + lp->r_a_tov = 2 * 2 * 1000; | |
1100 | + lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS | | |
1101 | + FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL); | |
1102 | + | |
1103 | + /* | |
1104 | + * allocate per cpu stats block | |
1105 | + */ | |
1106 | + for_each_online_cpu(i) { | |
1107 | + p = kzalloc(sizeof(struct fcoe_dev_stats), GFP_KERNEL); | |
1108 | + if (p) | |
1109 | + lp->dev_stats[i] = p; | |
1110 | + } | |
1111 | + | |
1112 | + /* Finish fc_lport configuration */ | |
1113 | + fc_lport_config(lp); | |
1114 | + | |
1115 | + return 0; | |
1116 | +} | |
1117 | + | |
1118 | +static int net_config(struct fc_lport *lp) | |
1119 | +{ | |
1120 | + u32 mfs; | |
1121 | + u64 wwnn, wwpn; | |
1122 | + struct net_device *net_dev; | |
1123 | + struct fcoe_softc *fc = (struct fcoe_softc *)lp->drv_priv; | |
1124 | + u8 flogi_maddr[ETH_ALEN]; | |
1125 | + | |
1126 | + /* Require support for get_pauseparam ethtool op. */ | |
1127 | + net_dev = fc->real_dev; | |
1128 | + if (!net_dev->ethtool_ops && (net_dev->priv_flags & IFF_802_1Q_VLAN)) | |
1129 | + net_dev = vlan_dev_real_dev(net_dev); | |
1130 | + if (!net_dev->ethtool_ops || !net_dev->ethtool_ops->get_pauseparam) | |
1131 | + return -EOPNOTSUPP; | |
1132 | + | |
1133 | + fc->phys_dev = net_dev; | |
1134 | + | |
1135 | + /* Do not support for bonding device */ | |
1136 | + if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) || | |
1137 | + (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) || | |
1138 | + (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) { | |
1139 | + return -EOPNOTSUPP; | |
1140 | + } | |
1141 | + | |
1142 | + /* | |
1143 | + * Determine max frame size based on underlying device and optional | |
1144 | + * user-configured limit. If the MFS is too low, fcoe_link_ok() | |
1145 | + * will return 0, so do this first. | |
1146 | + */ | |
1147 | + mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) + | |
1148 | + sizeof(struct fcoe_crc_eof)); | |
1149 | + fc_set_mfs(lp, mfs); | |
1150 | + | |
1151 | + lp->link_status = ~FC_PAUSE & ~FC_LINK_UP; | |
1152 | + if (!fcoe_link_ok(lp)) | |
1153 | + lp->link_status |= FC_LINK_UP; | |
1154 | + | |
1155 | + if (fc->real_dev->features & NETIF_F_SG) | |
1156 | + lp->capabilities = TRANS_C_SG; | |
1157 | + | |
1158 | + | |
1159 | + skb_queue_head_init(&fc->fcoe_pending_queue); | |
1160 | + | |
1161 | + memcpy(lp->ifname, fc->real_dev->name, IFNAMSIZ); | |
1162 | + | |
1163 | + /* setup Source Mac Address */ | |
1164 | + memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr, | |
1165 | + fc->real_dev->addr_len); | |
1166 | + | |
1167 | + wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0); | |
1168 | + fc_set_wwnn(lp, wwnn); | |
1169 | + /* XXX - 3rd arg needs to be vlan id */ | |
1170 | + wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0); | |
1171 | + fc_set_wwpn(lp, wwpn); | |
1172 | + | |
1173 | + /* | |
1174 | + * Add FCoE MAC address as second unicast MAC address | |
1175 | + * or enter promiscuous mode if not capable of listening | |
1176 | + * for multiple unicast MACs. | |
1177 | + */ | |
1178 | + rtnl_lock(); | |
1179 | + memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); | |
1180 | + dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN); | |
1181 | + rtnl_unlock(); | |
1182 | + | |
1183 | + /* | |
1184 | + * setup the receive function from ethernet driver | |
1185 | + * on the ethertype for the given device | |
1186 | + */ | |
1187 | + fc->fcoe_packet_type.func = fcoe_rcv; | |
1188 | + fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); | |
1189 | + fc->fcoe_packet_type.dev = fc->real_dev; | |
1190 | + dev_add_pack(&fc->fcoe_packet_type); | |
1191 | + | |
1192 | + return 0; | |
1193 | +} | |
1194 | + | |
1195 | +static void shost_config(struct fc_lport *lp) | |
1196 | +{ | |
1197 | + lp->host->max_lun = FCOE_MAX_LUN; | |
1198 | + lp->host->max_id = FCOE_MAX_FCP_TARGET; | |
1199 | + lp->host->max_channel = 0; | |
1200 | + lp->host->transportt = fcoe_transport_template; | |
1201 | +} | |
1202 | + | |
1203 | +static int libfc_config(struct fc_lport *lp) | |
1204 | +{ | |
1205 | + /* Set the function pointers set by the LLDD */ | |
1206 | + memcpy(&lp->tt, &fcoe_libfc_fcn_templ, | |
1207 | + sizeof(struct libfc_function_template)); | |
1208 | + | |
1209 | + if (fc_fcp_init(lp)) | |
1210 | + return -ENOMEM; | |
1211 | + fc_exch_init(lp); | |
1212 | + fc_lport_init(lp); | |
1213 | + fc_rport_init(lp); | |
1214 | + fc_ns_init(lp); | |
1215 | + fc_attr_init(lp); | |
1216 | + | |
1217 | + return 0; | |
1218 | +} | |
1219 | + | |
1220 | +/* | |
1221 | + * This function creates the fcoe interface | |
1222 | + * create struct fcdev which is a shared structure between opefc | |
1223 | + * and transport level protocol. | |
1224 | + */ | |
1225 | +int fcoe_create_interface(const char *ifname) | |
1226 | +{ | |
1227 | + struct fc_lport *lp = NULL; | |
1228 | + struct fcoe_softc *fc; | |
1229 | + struct net_device *net_dev; | |
1230 | + struct Scsi_Host *shost; | |
1231 | + struct fcoe_info *fci = &fcoei; | |
1232 | + int rc = 0; | |
1233 | + | |
1234 | + net_dev = dev_get_by_name(&init_net, ifname); | |
1235 | + if (net_dev == NULL) { | |
1236 | + FC_DBG("could not get network device for %s", | |
1237 | + ifname); | |
1238 | + return -ENODEV; | |
1239 | + } | |
1240 | + | |
1241 | + if (fcoe_find_fc_lport(net_dev->name) != NULL) { | |
1242 | + rc = -EEXIST; | |
1243 | + goto out_put_dev; | |
1244 | + } | |
1245 | + | |
1246 | + shost = scsi_host_alloc(&fcoe_driver_template, | |
1247 | + sizeof(struct fc_lport) + | |
1248 | + sizeof(struct fcoe_softc)); | |
1249 | + | |
1250 | + if (!shost) { | |
1251 | + FC_DBG("Could not allocate host structure\n"); | |
1252 | + rc = -ENOMEM; | |
1253 | + goto out_put_dev; | |
1254 | + } | |
1255 | + | |
1256 | + lp = shost_priv(shost); | |
1257 | + rc = lport_config(lp, shost); | |
1258 | + if (rc) | |
1259 | + goto out_host_put; | |
1260 | + | |
1261 | + /* Configure the fcoe_softc */ | |
1262 | + fc = (struct fcoe_softc *)lp->drv_priv; | |
1263 | + fc->lp = lp; | |
1264 | + fc->real_dev = net_dev; | |
1265 | + shost_config(lp); | |
1266 | + | |
1267 | + | |
1268 | + /* Add the new host to the SCSI-ml */ | |
1269 | + rc = scsi_add_host(lp->host, NULL); | |
1270 | + if (rc) { | |
1271 | + FC_DBG("error on scsi_add_host\n"); | |
1272 | + goto out_lp_destroy; | |
1273 | + } | |
1274 | + | |
1275 | + sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s", | |
1276 | + FCOE_DRIVER_NAME, FCOE_VERSION, | |
1277 | + ifname); | |
1278 | + | |
1279 | + /* Configure netdev and networking properties of the lp */ | |
1280 | + rc = net_config(lp); | |
1281 | + if (rc) | |
1282 | + goto out_lp_destroy; | |
1283 | + | |
1284 | + /* Initialize the library */ | |
1285 | + rc = libfc_config(lp); | |
1286 | + if (rc) | |
1287 | + goto out_lp_destroy; | |
1288 | + | |
1289 | + write_lock_bh(&fci->fcoe_hostlist_lock); | |
1290 | + list_add_tail(&fc->list, &fci->fcoe_hostlist); | |
1291 | + write_unlock_bh(&fci->fcoe_hostlist_lock); | |
1292 | + | |
1293 | + lp->boot_time = jiffies; | |
1294 | + | |
1295 | + fc_fabric_login(lp); | |
1296 | + | |
1297 | + return rc; | |
1298 | + | |
1299 | +out_lp_destroy: | |
1300 | + fc_exch_mgr_free(lp->emp); /* Free the EM */ | |
1301 | +out_host_put: | |
1302 | + scsi_host_put(lp->host); | |
1303 | +out_put_dev: | |
1304 | + dev_put(net_dev); | |
1305 | + return rc; | |
1306 | +} | |
1307 | + | |
1308 | +void fcoe_clean_pending_queue(struct fc_lport *lp) | |
1309 | +{ | |
1310 | + struct fcoe_softc *fc = lp->drv_priv; | |
1311 | + struct sk_buff *skb; | |
1312 | + | |
1313 | + spin_lock_bh(&fc->fcoe_pending_queue.lock); | |
1314 | + while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) { | |
1315 | + spin_unlock_bh(&fc->fcoe_pending_queue.lock); | |
1316 | + kfree_skb(skb); | |
1317 | + spin_lock_bh(&fc->fcoe_pending_queue.lock); | |
1318 | + } | |
1319 | + spin_unlock_bh(&fc->fcoe_pending_queue.lock); | |
1320 | +} | |
1321 | diff --git a/drivers/scsi/fcoe/fcoeinit.c b/drivers/scsi/fcoe/fcoeinit.c | |
1322 | new file mode 100644 | |
1323 | index 0000000..e069835 | |
1324 | --- /dev/null | |
1325 | +++ b/drivers/scsi/fcoe/fcoeinit.c | |
1326 | @@ -0,0 +1,440 @@ | |
1327 | +/* | |
1328 | + * Copyright(c) 2007 Intel Corporation. All rights reserved. | |
1329 | + * | |
1330 | + * This program is free software; you can redistribute it and/or modify it | |
1331 | + * under the terms and conditions of the GNU General Public License, | |
1332 | + * version 2, as published by the Free Software Foundation. | |
1333 | + * | |
1334 | + * This program is distributed in the hope it will be useful, but WITHOUT | |
1335 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
1336 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
1337 | + * more details. | |
1338 | + * | |
1339 | + * You should have received a copy of the GNU General Public License along with | |
1340 | + * this program; if not, write to the Free Software Foundation, Inc., | |
1341 | + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | |
1342 | + * | |
1343 | + * Maintained at www.Open-FCoE.org | |
1344 | + */ | |
1345 | + | |
1346 | +#include <linux/module.h> | |
1347 | +#include <linux/version.h> | |
1348 | +#include <linux/kernel.h> | |
1349 | +#include <linux/kthread.h> | |
1350 | +#include <linux/spinlock.h> | |
1351 | +#include <linux/cpu.h> | |
1352 | +#include <linux/netdevice.h> | |
1353 | +#include <linux/etherdevice.h> | |
1354 | +#include <linux/ethtool.h> | |
1355 | +#include <linux/if_ether.h> | |
1356 | +#include <linux/fs.h> | |
1357 | +#include <linux/sysfs.h> | |
1358 | +#include <linux/ctype.h> | |
1359 | + | |
1360 | +#include <scsi/libfc/libfc.h> | |
1361 | + | |
1362 | +#include "fcoe_def.h" | |
1363 | + | |
1364 | +MODULE_AUTHOR("Open-FCoE.org"); | |
1365 | +MODULE_DESCRIPTION("FCoE"); | |
1366 | +MODULE_LICENSE("GPL"); | |
1367 | + | |
1368 | +/* | |
1369 | + * Static functions and variables definations | |
1370 | + */ | |
1371 | +#ifdef CONFIG_HOTPLUG_CPU | |
1372 | +static int fcoe_cpu_callback(struct notifier_block *, ulong, void *); | |
1373 | +#endif /* CONFIG_HOTPLUG_CPU */ | |
1374 | +static int fcoe_device_notification(struct notifier_block *, ulong, void *); | |
1375 | +static void fcoe_dev_setup(void); | |
1376 | +static void fcoe_dev_cleanup(void); | |
1377 | + | |
1378 | +struct scsi_transport_template *fcoe_transport_template; | |
1379 | + | |
1380 | +static int fcoe_reset(struct Scsi_Host *shost) | |
1381 | +{ | |
1382 | + struct fc_lport *lp = shost_priv(shost); | |
1383 | + fc_lport_enter_reset(lp); | |
1384 | + return 0; | |
1385 | +} | |
1386 | + | |
1387 | +struct fc_function_template fcoe_transport_function = { | |
1388 | + .show_host_node_name = 1, | |
1389 | + .show_host_port_name = 1, | |
1390 | + .show_host_supported_classes = 1, | |
1391 | + .show_host_supported_fc4s = 1, | |
1392 | + .show_host_active_fc4s = 1, | |
1393 | + .show_host_maxframe_size = 1, | |
1394 | + | |
1395 | + .get_host_port_id = fc_get_host_port_id, | |
1396 | + .show_host_port_id = 1, | |
1397 | + .get_host_speed = fc_get_host_speed, | |
1398 | + .show_host_speed = 1, | |
1399 | + .get_host_port_type = fc_get_host_port_type, | |
1400 | + .show_host_port_type = 1, | |
1401 | + .get_host_port_state = fc_get_host_port_state, | |
1402 | + .show_host_port_state = 1, | |
1403 | + .show_host_symbolic_name = 1, | |
1404 | + | |
1405 | + .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv), | |
1406 | + .show_rport_maxframe_size = 1, | |
1407 | + .show_rport_supported_classes = 1, | |
1408 | + | |
1409 | + .get_host_fabric_name = fc_get_host_fabric_name, | |
1410 | + .show_host_fabric_name = 1, | |
1411 | + .show_starget_node_name = 1, | |
1412 | + .show_starget_port_name = 1, | |
1413 | + .show_starget_port_id = 1, | |
1414 | + .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo, | |
1415 | + .show_rport_dev_loss_tmo = 1, | |
1416 | + .get_fc_host_stats = fc_get_host_stats, | |
1417 | + .issue_fc_host_lip = fcoe_reset, | |
1418 | +}; | |
1419 | + | |
1420 | +struct fcoe_percpu_s *fcoe_percpu[NR_CPUS]; | |
1421 | + | |
1422 | +#ifdef CONFIG_HOTPLUG_CPU | |
1423 | +static struct notifier_block fcoe_cpu_notifier = { | |
1424 | + .notifier_call = fcoe_cpu_callback, | |
1425 | +}; | |
1426 | +#endif /* CONFIG_HOTPLUG_CPU */ | |
1427 | + | |
1428 | +/* | |
1429 | + * notification function from net device | |
1430 | + */ | |
1431 | +static struct notifier_block fcoe_notifier = { | |
1432 | + .notifier_call = fcoe_device_notification, | |
1433 | +}; | |
1434 | + | |
1435 | +#ifdef CONFIG_HOTPLUG_CPU | |
1436 | +/* | |
1437 | + * create percpu stats block | |
1438 | + * called by cpu add/remove notifier | |
1439 | + */ | |
1440 | +static void fcoe_create_percpu_data(int cpu) | |
1441 | +{ | |
1442 | + struct fc_lport *lp; | |
1443 | + struct fcoe_softc *fc; | |
1444 | + struct fcoe_dev_stats *p; | |
1445 | + struct fcoe_info *fci = &fcoei; | |
1446 | + | |
1447 | + write_lock_bh(&fci->fcoe_hostlist_lock); | |
1448 | + list_for_each_entry(fc, &fci->fcoe_hostlist, list) { | |
1449 | + lp = fc->lp; | |
1450 | + if (lp->dev_stats[cpu] == NULL) { | |
1451 | + p = kzalloc(sizeof(struct fcoe_dev_stats), GFP_KERNEL); | |
1452 | + if (p) | |
1453 | + lp->dev_stats[cpu] = p; | |
1454 | + } | |
1455 | + } | |
1456 | + write_unlock_bh(&fci->fcoe_hostlist_lock); | |
1457 | +} | |
1458 | + | |
1459 | +/* | |
1460 | + * destroy percpu stats block | |
1461 | + * called by cpu add/remove notifier | |
1462 | + */ | |
1463 | +static void fcoe_destroy_percpu_data(int cpu) | |
1464 | +{ | |
1465 | + struct fcoe_dev_stats *p; | |
1466 | + struct fc_lport *lp; | |
1467 | + struct fcoe_softc *fc; | |
1468 | + struct fcoe_info *fci = &fcoei; | |
1469 | + | |
1470 | + write_lock_bh(&fci->fcoe_hostlist_lock); | |
1471 | + list_for_each_entry(fc, &fci->fcoe_hostlist, list) { | |
1472 | + lp = fc->lp; | |
1473 | + p = lp->dev_stats[cpu]; | |
1474 | + if (p != NULL) { | |
1475 | + lp->dev_stats[cpu] = NULL; | |
1476 | + kfree(p); | |
1477 | + } | |
1478 | + } | |
1479 | + write_unlock_bh(&fci->fcoe_hostlist_lock); | |
1480 | +} | |
1481 | + | |
1482 | +/* | |
1483 | + * Get notified when a cpu comes on/off. Be hotplug friendly. | |
1484 | + */ | |
1485 | +static int fcoe_cpu_callback(struct notifier_block *nfb, unsigned long action, | |
1486 | + void *hcpu) | |
1487 | +{ | |
1488 | + unsigned int cpu = (unsigned long)hcpu; | |
1489 | + | |
1490 | + switch (action) { | |
1491 | + case CPU_ONLINE: | |
1492 | + fcoe_create_percpu_data(cpu); | |
1493 | + break; | |
1494 | + case CPU_DEAD: | |
1495 | + fcoe_destroy_percpu_data(cpu); | |
1496 | + break; | |
1497 | + default: | |
1498 | + break; | |
1499 | + } | |
1500 | + return NOTIFY_OK; | |
1501 | +} | |
1502 | +#endif /* CONFIG_HOTPLUG_CPU */ | |
1503 | + | |
1504 | +/* | |
1505 | + * function to setup link change notification interface | |
1506 | + */ | |
1507 | +static void fcoe_dev_setup(void) | |
1508 | +{ | |
1509 | + /* | |
1510 | + * here setup a interface specific wd time to | |
1511 | + * monitor the link state | |
1512 | + */ | |
1513 | + register_netdevice_notifier(&fcoe_notifier); | |
1514 | +} | |
1515 | + | |
1516 | +/* | |
1517 | + * function to cleanup link change notification interface | |
1518 | + */ | |
1519 | +static void fcoe_dev_cleanup(void) | |
1520 | +{ | |
1521 | + unregister_netdevice_notifier(&fcoe_notifier); | |
1522 | +} | |
1523 | + | |
1524 | +/* | |
1525 | + * This function is called by the ethernet driver | |
1526 | + * this is called in case of link change event | |
1527 | + */ | |
1528 | +static int fcoe_device_notification(struct notifier_block *notifier, | |
1529 | + ulong event, void *ptr) | |
1530 | +{ | |
1531 | + struct fc_lport *lp = NULL; | |
1532 | + struct net_device *real_dev = ptr; | |
1533 | + struct fcoe_softc *fc; | |
1534 | + struct fcoe_dev_stats *stats; | |
1535 | + struct fcoe_info *fci = &fcoei; | |
1536 | + u16 new_status; | |
1537 | + u32 mfs; | |
1538 | + int rc = NOTIFY_OK; | |
1539 | + | |
1540 | + read_lock(&fci->fcoe_hostlist_lock); | |
1541 | + list_for_each_entry(fc, &fci->fcoe_hostlist, list) { | |
1542 | + if (fc->real_dev == real_dev) { | |
1543 | + lp = fc->lp; | |
1544 | + break; | |
1545 | + } | |
1546 | + } | |
1547 | + read_unlock(&fci->fcoe_hostlist_lock); | |
1548 | + if (lp == NULL) { | |
1549 | + rc = NOTIFY_DONE; | |
1550 | + goto out; | |
1551 | + } | |
1552 | + | |
1553 | + new_status = lp->link_status; | |
1554 | + switch (event) { | |
1555 | + case NETDEV_DOWN: | |
1556 | + case NETDEV_GOING_DOWN: | |
1557 | + new_status &= ~FC_LINK_UP; | |
1558 | + break; | |
1559 | + case NETDEV_UP: | |
1560 | + case NETDEV_CHANGE: | |
1561 | + new_status &= ~FC_LINK_UP; | |
1562 | + if (!fcoe_link_ok(lp)) | |
1563 | + new_status |= FC_LINK_UP; | |
1564 | + break; | |
1565 | + case NETDEV_CHANGEMTU: | |
1566 | + mfs = fc->real_dev->mtu - | |
1567 | + (sizeof(struct fcoe_hdr) + | |
1568 | + sizeof(struct fcoe_crc_eof)); | |
1569 | + if (fc->user_mfs && fc->user_mfs < mfs) | |
1570 | + mfs = fc->user_mfs; | |
1571 | + if (mfs >= FC_MIN_MAX_FRAME) | |
1572 | + fc_set_mfs(lp, mfs); | |
1573 | + new_status &= ~FC_LINK_UP; | |
1574 | + if (!fcoe_link_ok(lp)) | |
1575 | + new_status |= FC_LINK_UP; | |
1576 | + break; | |
1577 | + case NETDEV_REGISTER: | |
1578 | + break; | |
1579 | + default: | |
1580 | + FC_DBG("unknown event %ld call", event); | |
1581 | + } | |
1582 | + if (lp->link_status != new_status) { | |
1583 | + if ((new_status & FC_LINK_UP) == FC_LINK_UP) | |
1584 | + fc_linkup(lp); | |
1585 | + else { | |
1586 | + stats = lp->dev_stats[smp_processor_id()]; | |
1587 | + stats->LinkFailureCount++; | |
1588 | + fc_linkdown(lp); | |
1589 | + fcoe_clean_pending_queue(lp); | |
1590 | + } | |
1591 | + } | |
1592 | +out: | |
1593 | + return rc; | |
1594 | +} | |
1595 | + | |
1596 | +static void trimstr(char *str, int len) | |
1597 | +{ | |
1598 | + char *cp = str + len; | |
1599 | + while (--cp >= str && *cp == '\n') | |
1600 | + *cp = '\0'; | |
1601 | +} | |
1602 | + | |
1603 | +static ssize_t fcoe_destroy(struct kobject *kobj, struct kobj_attribute *attr, | |
1604 | + const char *buffer, size_t size) | |
1605 | +{ | |
1606 | + char ifname[40]; | |
1607 | + strcpy(ifname, buffer); | |
1608 | + trimstr(ifname, strlen(ifname)); | |
1609 | + fcoe_destroy_interface(ifname); | |
1610 | + return size; | |
1611 | +} | |
1612 | + | |
1613 | +static ssize_t fcoe_create(struct kobject *kobj, struct kobj_attribute *attr, | |
1614 | + const char *buffer, size_t size) | |
1615 | +{ | |
1616 | + char ifname[40]; | |
1617 | + strcpy(ifname, buffer); | |
1618 | + trimstr(ifname, strlen(ifname)); | |
1619 | + fcoe_create_interface(ifname); | |
1620 | + return size; | |
1621 | +} | |
1622 | + | |
1623 | +static const struct kobj_attribute fcoe_destroyattr = \ | |
1624 | + __ATTR(destroy, S_IWUSR, NULL, fcoe_destroy); | |
1625 | +static const struct kobj_attribute fcoe_createattr = \ | |
1626 | + __ATTR(create, S_IWUSR, NULL, fcoe_create); | |
1627 | + | |
1628 | +/* | |
1629 | + * Initialization routine | |
1630 | + * 1. Will create fc transport software structure | |
1631 | + * 2. initialize the link list of port information structure | |
1632 | + */ | |
1633 | +static int __init fcoeinit(void) | |
1634 | +{ | |
1635 | + int rc = 0; | |
1636 | + int cpu; | |
1637 | + struct fcoe_percpu_s *p; | |
1638 | + struct fcoe_info *fci = &fcoei; | |
1639 | + | |
1640 | + rc = sysfs_create_file(&THIS_MODULE->mkobj.kobj, | |
1641 | + &fcoe_destroyattr.attr); | |
1642 | + if (!rc) | |
1643 | + rc = sysfs_create_file(&THIS_MODULE->mkobj.kobj, | |
1644 | + &fcoe_createattr.attr); | |
1645 | + | |
1646 | + if (rc) | |
1647 | + return rc; | |
1648 | + | |
1649 | + rwlock_init(&fci->fcoe_hostlist_lock); | |
1650 | + | |
1651 | +#ifdef CONFIG_HOTPLUG_CPU | |
1652 | + register_cpu_notifier(&fcoe_cpu_notifier); | |
1653 | +#endif /* CONFIG_HOTPLUG_CPU */ | |
1654 | + | |
1655 | + /* | |
1656 | + * initialize per CPU interrupt thread | |
1657 | + */ | |
1658 | + for_each_online_cpu(cpu) { | |
1659 | + p = kzalloc(sizeof(struct fcoe_percpu_s), GFP_KERNEL); | |
1660 | + if (p) { | |
1661 | + p->thread = kthread_create(fcoe_percpu_receive_thread, | |
1662 | + (void *)p, | |
1663 | + "fcoethread/%d", cpu); | |
1664 | + | |
1665 | + /* | |
1666 | + * if there is no error then bind the thread to the cpu | |
1667 | + * initialize the semaphore and skb queue head | |
1668 | + */ | |
1669 | + if (likely(!IS_ERR(p->thread))) { | |
1670 | + p->cpu = cpu; | |
1671 | + fci->fcoe_percpu[cpu] = p; | |
1672 | + skb_queue_head_init(&p->fcoe_rx_list); | |
1673 | + kthread_bind(p->thread, cpu); | |
1674 | + wake_up_process(p->thread); | |
1675 | + } else { | |
1676 | + fci->fcoe_percpu[cpu] = NULL; | |
1677 | + kfree(p); | |
1678 | + | |
1679 | + } | |
1680 | + } | |
1681 | + } | |
1682 | + if (rc < 0) { | |
1683 | + FC_DBG("failed to initialize proc intrerface\n"); | |
1684 | + rc = -ENODEV; | |
1685 | + goto out_chrdev; | |
1686 | + } | |
1687 | + | |
1688 | + /* | |
1689 | + * setup link change notification | |
1690 | + */ | |
1691 | + fcoe_dev_setup(); | |
1692 | + | |
1693 | + init_timer(&fci->timer); | |
1694 | + fci->timer.data = (ulong) fci; | |
1695 | + fci->timer.function = fcoe_watchdog; | |
1696 | + fci->timer.expires = (jiffies + (10 * HZ)); | |
1697 | + add_timer(&fci->timer); | |
1698 | + | |
1699 | + fcoe_transport_template = | |
1700 | + fc_attach_transport(&fcoe_transport_function); | |
1701 | + | |
1702 | + if (fcoe_transport_template == NULL) { | |
1703 | + FC_DBG("fail to attach fc transport"); | |
1704 | + return -1; | |
1705 | + } | |
1706 | + | |
1707 | + return 0; | |
1708 | + | |
1709 | +out_chrdev: | |
1710 | +#ifdef CONFIG_HOTPLUG_CPU | |
1711 | + unregister_cpu_notifier(&fcoe_cpu_notifier); | |
1712 | +#endif /* CONFIG_HOTPLUG_CPU */ | |
1713 | + return rc; | |
1714 | +} | |
1715 | + | |
1716 | +static void __exit fcoe_exit(void) | |
1717 | +{ | |
1718 | + u32 idx; | |
1719 | + struct fcoe_softc *fc, *tmp; | |
1720 | + struct fc_lport *lp; | |
1721 | + struct fcoe_info *fci = &fcoei; | |
1722 | + struct fcoe_percpu_s *p; | |
1723 | + struct sk_buff *skb; | |
1724 | + | |
1725 | + /* | |
1726 | + * Stop all call back interfaces | |
1727 | + */ | |
1728 | +#ifdef CONFIG_HOTPLUG_CPU | |
1729 | + unregister_cpu_notifier(&fcoe_cpu_notifier); | |
1730 | +#endif /* CONFIG_HOTPLUG_CPU */ | |
1731 | + fcoe_dev_cleanup(); | |
1732 | + | |
1733 | + /* | |
1734 | + * stop timer | |
1735 | + */ | |
1736 | + del_timer_sync(&fci->timer); | |
1737 | + | |
1738 | + /* | |
1739 | + * assuming that at this time there will be no | |
1740 | + * ioctl in prograss, therefore we do not need to lock the | |
1741 | + * list. | |
1742 | + */ | |
1743 | + list_for_each_entry_safe(fc, tmp, &fci->fcoe_hostlist, list) { | |
1744 | + lp = fc->lp; | |
1745 | + fcoe_destroy_interface(lp->ifname); | |
1746 | + } | |
1747 | + | |
1748 | + for (idx = 0; idx < NR_CPUS; idx++) { | |
1749 | + if (fci->fcoe_percpu[idx]) { | |
1750 | + kthread_stop(fci->fcoe_percpu[idx]->thread); | |
1751 | + p = fci->fcoe_percpu[idx]; | |
1752 | + spin_lock_bh(&p->fcoe_rx_list.lock); | |
1753 | + while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL) | |
1754 | + kfree_skb(skb); | |
1755 | + spin_unlock_bh(&p->fcoe_rx_list.lock); | |
1756 | + if (fci->fcoe_percpu[idx]->crc_eof_page) | |
1757 | + put_page(fci->fcoe_percpu[idx]->crc_eof_page); | |
1758 | + kfree(fci->fcoe_percpu[idx]); | |
1759 | + } | |
1760 | + } | |
1761 | + | |
1762 | + fc_release_transport(fcoe_transport_template); | |
1763 | +} | |
1764 | + | |
1765 | +module_init(fcoeinit); | |
1766 | +module_exit(fcoe_exit); | |
1767 | -- | |
1768 | 1.5.2.4 | |
1769 |