Parcourir la source

[fcoe] Add support for Fibre Channel over Ethernet

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown il y a 14 ans
Parent
révision
dace106f82

+ 3
- 0
src/config/config_ethernet.c Voir le fichier

@@ -21,3 +21,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
21 21
 #ifdef SANBOOT_PROTO_AOE
22 22
 REQUIRE_OBJECT ( aoe );
23 23
 #endif
24
+#ifdef NET_PROTO_FCOE
25
+REQUIRE_OBJECT ( fcoe );
26
+#endif

+ 1
- 0
src/config/general.h Voir le fichier

@@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
40 40
  */
41 41
 
42 42
 #define	NET_PROTO_IPV4		/* IPv4 protocol */
43
+#undef	NET_PROTO_FCOE		/* Fibre Channel over Ethernet protocol */
43 44
 
44 45
 /*
45 46
  * PXE support

+ 1
- 0
src/include/ipxe/errfile.h Voir le fichier

@@ -187,6 +187,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
187 187
 #define ERRFILE_fc			( ERRFILE_NET | 0x002b0000 )
188 188
 #define ERRFILE_fcels			( ERRFILE_NET | 0x002c0000 )
189 189
 #define ERRFILE_fcp			( ERRFILE_NET | 0x002d0000 )
190
+#define ERRFILE_fcoe			( ERRFILE_NET | 0x002e0000 )
190 191
 
191 192
 #define ERRFILE_image		      ( ERRFILE_IMAGE | 0x00000000 )
192 193
 #define ERRFILE_elf		      ( ERRFILE_IMAGE | 0x00010000 )

+ 76
- 0
src/include/ipxe/fcoe.h Voir le fichier

@@ -0,0 +1,76 @@
1
+#ifndef _IPXE_FCOE_H
2
+#define _IPXE_FCOE_H
3
+
4
+/**
5
+ * @file
6
+ *
7
+ * Fibre Channel over Ethernet
8
+ *
9
+ */
10
+
11
+FILE_LICENCE ( GPL2_OR_LATER );
12
+
13
+#include <stdint.h>
14
+#include <ipxe/fc.h>
15
+#include <ipxe/if_ether.h>
16
+
17
+/** An FCoE name */
18
+union fcoe_name {
19
+	/** Fibre Channel name */
20
+	struct fc_name fc;
21
+	/** FCoE name */
22
+	struct {
23
+		/** Naming authority */
24
+		uint16_t authority;
25
+		/** MAC address */
26
+		uint8_t mac[ETH_ALEN];
27
+	} __attribute__ (( packed )) fcoe;
28
+};
29
+
30
+/** IEEE 48-bit address */
31
+#define FCOE_AUTHORITY_IEEE 0x1000
32
+
33
+/** IEEE extended */
34
+#define FCOE_AUTHORITY_IEEE_EXTENDED 0x2000
35
+
36
+/** An FCoE header */
37
+struct fcoe_header {
38
+	/** FCoE frame version */
39
+	uint8_t version;
40
+	/** Reserved */
41
+	uint8_t reserved[12];
42
+	/** Start of Frame marker */
43
+	uint8_t sof;
44
+} __attribute__ (( packed ));
45
+
46
+/** FCoE frame version */
47
+#define FCOE_FRAME_VER 0x00
48
+
49
+/** Start of Frame marker values */
50
+enum fcoe_sof {
51
+	FCOE_SOF_F = 0x28,	/**< Start of Frame Class F */
52
+	FCOE_SOF_I2 = 0x2d,	/**< Start of Frame Initiate Class 2 */
53
+	FCOE_SOF_N2 = 0x35,	/**< Start of Frame Normal Class 2 */
54
+	FCOE_SOF_I3 = 0x2e,	/**< Start of Frame Initiate Class 3 */
55
+	FCOE_SOF_N3 = 0x36,	/**< Start of Frame Normal Class 3 */
56
+};
57
+
58
+/** An FCoE footer */
59
+struct fcoe_footer {
60
+	/** CRC */
61
+	uint32_t crc;
62
+	/** End of frame marker */
63
+	uint8_t eof;
64
+	/** Reserved */
65
+	uint8_t reserved[3];
66
+} __attribute__ (( packed ));
67
+
68
+/** End of Frame marker value */
69
+enum fcoe_eof {
70
+	FCOE_EOF_N = 0x41,	/**< End of Frame Normal */
71
+	FCOE_EOF_T = 0x42,	/**< End of Frame Terminate */
72
+	FCOE_EOF_NI = 0x49,	/**< End of Frame Invalid */
73
+	FCOE_EOF_A = 0x50,	/**< End of Frame Abort */
74
+};
75
+
76
+#endif /* _IPXE_FCOE_H */

+ 1
- 0
src/include/ipxe/features.h Voir le fichier

@@ -50,6 +50,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
50 50
 #define DHCP_EB_FEATURE_ELF		0x22 /**< ELF format */
51 51
 #define DHCP_EB_FEATURE_COMBOOT		0x23 /**< COMBOOT format */
52 52
 #define DHCP_EB_FEATURE_EFI		0x24 /**< EFI format */
53
+#define DHCP_EB_FEATURE_FCOE		0x25 /**< FCoE protocol */
53 54
 
54 55
 /** @} */
55 56
 

+ 2
- 0
src/include/ipxe/if_ether.h Voir le fichier

@@ -22,6 +22,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
22 22
 #define ETH_P_SLOW	0x8809	/* Ethernet slow protocols */
23 23
 #define ETH_P_EAPOL	0x888E	/* 802.1X EAP over LANs */
24 24
 #define ETH_P_AOE	0x88A2	/* ATA over Ethernet */
25
+#define ETH_P_FCOE	0x8906	/* Fibre Channel over Ethernet */
26
+#define ETH_P_FIP	0x8914	/* FCoE Initialization Protocol */
25 27
 
26 28
 /** An Ethernet link-layer header */
27 29
 struct ethhdr {

+ 383
- 0
src/net/fcoe.c Voir le fichier

@@ -0,0 +1,383 @@
1
+/*
2
+ * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+FILE_LICENCE ( GPL2_OR_LATER );
20
+
21
+#include <stddef.h>
22
+#include <stdlib.h>
23
+#include <errno.h>
24
+#include <byteswap.h>
25
+#include <ipxe/if_ether.h>
26
+#include <ipxe/if_arp.h>
27
+#include <ipxe/iobuf.h>
28
+#include <ipxe/interface.h>
29
+#include <ipxe/xfer.h>
30
+#include <ipxe/netdevice.h>
31
+#include <ipxe/features.h>
32
+#include <ipxe/crc32.h>
33
+#include <ipxe/fc.h>
34
+#include <ipxe/fcoe.h>
35
+
36
+/** @file
37
+ *
38
+ * FCoE protocol
39
+ *
40
+ */
41
+
42
+FEATURE ( FEATURE_PROTOCOL, "FCoE", DHCP_EB_FEATURE_FCOE, 1 );
43
+
44
+/** An FCoE port */
45
+struct fcoe_port {
46
+	/** Reference count */
47
+	struct refcnt refcnt;
48
+	/** List of FCoE ports */
49
+	struct list_head list;
50
+	/** Transport interface */
51
+	struct interface transport;
52
+	/** Network device */
53
+	struct net_device *netdev;
54
+	/** FCoE forwarder MAC address */
55
+	uint8_t fcf_ll_addr[ETH_ALEN];
56
+};
57
+
58
+/** List of FCoE ports */
59
+static LIST_HEAD ( fcoe_ports );
60
+
61
+struct net_protocol fcoe_protocol __net_protocol;
62
+
63
+/** Default FCoE forwarded MAC address */
64
+uint8_t fcoe_default_fcf_ll_addr[ETH_ALEN] =
65
+	{ 0x0e, 0xfc, 0x00, 0xff, 0xff, 0xfe };
66
+
67
+/**
68
+ * Identify FCoE port by network device
69
+ *
70
+ * @v netdev		Network device
71
+ * @ret fcoe		FCoE port, or NULL
72
+ */
73
+static struct fcoe_port * fcoe_demux ( struct net_device *netdev ) {
74
+	struct fcoe_port *fcoe;
75
+
76
+	list_for_each_entry ( fcoe, &fcoe_ports, list ) {
77
+		if ( fcoe->netdev == netdev )
78
+			return fcoe;
79
+	}
80
+	return NULL;
81
+}
82
+
83
+/**
84
+ * Transmit FCoE packet
85
+ *
86
+ * @v fcoe		FCoE port
87
+ * @v iobuf		I/O buffer
88
+ * @v meta		Data transfer metadata
89
+ * @ret rc		Return status code
90
+ */
91
+static int fcoe_deliver ( struct fcoe_port *fcoe,
92
+			  struct io_buffer *iobuf,
93
+			  struct xfer_metadata *meta __unused ) {
94
+	struct fc_frame_header *fchdr = iobuf->data;
95
+	struct fcoe_header *fcoehdr;
96
+	struct fcoe_footer *fcoeftr;
97
+	uint32_t crc;
98
+	int rc;
99
+
100
+	/* Calculate CRC */
101
+	crc = crc32_le ( ~((uint32_t)0), iobuf->data, iob_len ( iobuf ) );
102
+
103
+	/* Create FCoE header */
104
+	fcoehdr = iob_push ( iobuf, sizeof ( *fcoehdr ) );
105
+	memset ( fcoehdr, 0, sizeof ( *fcoehdr ) );
106
+	fcoehdr->sof = ( ( fchdr->seq_cnt == ntohs ( 0 ) ) ?
107
+			 FCOE_SOF_I3 : FCOE_SOF_N3 );
108
+	fcoeftr = iob_put ( iobuf, sizeof ( *fcoeftr ) );
109
+	memset ( fcoeftr, 0, sizeof ( *fcoeftr ) );
110
+	fcoeftr->crc = cpu_to_le32 ( crc ^ ~((uint32_t)0) );
111
+	fcoeftr->eof = ( ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) ?
112
+			 FCOE_EOF_T : FCOE_EOF_N );
113
+
114
+	/* Transmit packet */
115
+	if ( ( rc = net_tx ( iob_disown ( iobuf ), fcoe->netdev, &fcoe_protocol,
116
+			     fcoe->fcf_ll_addr ) ) != 0 ) {
117
+		DBGC ( fcoe, "FCoE %s could not transmit: %s\n",
118
+		       fcoe->netdev->name, strerror ( rc ) );
119
+		goto done;
120
+	}
121
+
122
+ done:
123
+	free_iob ( iobuf );
124
+	return rc;
125
+}
126
+
127
+/**
128
+ * Allocate FCoE I/O buffer
129
+ *
130
+ * @v len		Payload length
131
+ * @ret iobuf		I/O buffer, or NULL
132
+ */
133
+static struct io_buffer * fcoe_alloc_iob ( struct fcoe_port *fcoe __unused,
134
+					   size_t len ) {
135
+	struct io_buffer *iobuf;
136
+
137
+	iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( struct fcoe_header ) +
138
+			    len + sizeof ( struct fcoe_footer ) );
139
+	if ( iobuf ) {
140
+		iob_reserve ( iobuf, ( MAX_LL_HEADER_LEN +
141
+				       sizeof ( struct fcoe_header ) ) );
142
+	}
143
+	return iobuf;
144
+}
145
+
146
+/**
147
+ * Process incoming FCoE packets
148
+ *
149
+ * @v iobuf		I/O buffer
150
+ * @v netdev		Network device
151
+ * @v ll_source		Link-layer source address
152
+ * @ret rc		Return status code
153
+ */
154
+static int fcoe_rx ( struct io_buffer *iobuf,
155
+		     struct net_device *netdev,
156
+		     const void *ll_source ) {
157
+	struct fcoe_header *fcoehdr;
158
+	struct fcoe_footer *fcoeftr;
159
+	struct fcoe_port *fcoe;
160
+	int rc;
161
+
162
+	/* Identify FCoE port */
163
+	if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) {
164
+		DBG ( "FCoE received frame for net device %s missing FCoE "
165
+		      "port\n", netdev->name );
166
+		rc = -ENOTCONN;
167
+		goto done;
168
+	}
169
+
170
+	/* Sanity check */
171
+	if ( iob_len ( iobuf ) < ( sizeof ( *fcoehdr ) + sizeof ( *fcoeftr ) )){
172
+		DBGC ( fcoe, "FCoE %s received under-length frame (%zd "
173
+		       "bytes)\n", fcoe->netdev->name, iob_len ( iobuf ) );
174
+		rc = -EINVAL;
175
+		goto done;
176
+	}
177
+
178
+	/* Strip header and footer */
179
+	fcoehdr = iobuf->data;
180
+	iob_pull ( iobuf, sizeof ( *fcoehdr ) );
181
+	fcoeftr = ( iobuf->data + iob_len ( iobuf ) - sizeof ( *fcoeftr ) );
182
+	iob_unput ( iobuf, sizeof ( *fcoeftr ) );
183
+
184
+	/* Validity checks */
185
+	if ( fcoehdr->version != FCOE_FRAME_VER ) {
186
+		DBGC ( fcoe, "FCoE %s received unsupported frame version "
187
+		       "%02x\n", fcoe->netdev->name, fcoehdr->version );
188
+		rc = -EPROTONOSUPPORT;
189
+		goto done;
190
+	}
191
+	if ( ! ( ( fcoehdr->sof == FCOE_SOF_I3 ) ||
192
+		 ( fcoehdr->sof == FCOE_SOF_N3 ) ) ) {
193
+		DBGC ( fcoe, "FCoE %s received unsupported start-of-frame "
194
+		       "delimiter %02x\n", fcoe->netdev->name, fcoehdr->sof );
195
+		rc = -EINVAL;
196
+		goto done;
197
+	}
198
+	if ( ( le32_to_cpu ( fcoeftr->crc ) ^ ~((uint32_t)0) ) !=
199
+	     crc32_le ( ~((uint32_t)0), iobuf->data, iob_len ( iobuf ) ) ) {
200
+		DBGC ( fcoe, "FCoE %s received invalid CRC\n",
201
+		       fcoe->netdev->name );
202
+		rc = -EINVAL;
203
+		goto done;
204
+	}
205
+	if ( ! ( ( fcoeftr->eof == FCOE_EOF_N ) ||
206
+		 ( fcoeftr->eof == FCOE_EOF_T ) ) ) {
207
+		DBGC ( fcoe, "FCoE %s received unsupported end-of-frame "
208
+		       "delimiter %02x\n", fcoe->netdev->name, fcoeftr->eof );
209
+		rc = -EINVAL;
210
+		goto done;
211
+	}
212
+
213
+	/* Record FCF address */
214
+	memcpy ( &fcoe->fcf_ll_addr, ll_source, sizeof ( fcoe->fcf_ll_addr ) );
215
+
216
+	/* Hand off via transport interface */
217
+	if ( ( rc = xfer_deliver_iob ( &fcoe->transport,
218
+				       iob_disown ( iobuf ) ) ) != 0 ) {
219
+		DBGC ( fcoe, "FCoE %s could not deliver frame: %s\n",
220
+		       fcoe->netdev->name, strerror ( rc ) );
221
+		goto done;
222
+	}
223
+
224
+ done:
225
+	free_iob ( iobuf );
226
+	return rc;
227
+}
228
+
229
+/**
230
+ * Check FCoE flow control window
231
+ *
232
+ * @v fcoe		FCoE port
233
+ * @ret len		Length of window
234
+ */
235
+static size_t fcoe_window ( struct fcoe_port *fcoe ) {
236
+	struct net_device *netdev = fcoe->netdev;
237
+
238
+	return ( ( netdev_is_open ( netdev ) && netdev_link_ok ( netdev ) ) ?
239
+		 ~( ( size_t ) 0 ) : 0 );
240
+}
241
+
242
+/**
243
+ * Close FCoE port
244
+ *
245
+ * @v fcoe		FCoE port
246
+ * @v rc		Reason for close
247
+ */
248
+static void fcoe_close ( struct fcoe_port *fcoe, int rc ) {
249
+
250
+	intf_shutdown ( &fcoe->transport, rc );
251
+	netdev_put ( fcoe->netdev );
252
+	list_del ( &fcoe->list );
253
+	ref_put ( &fcoe->refcnt );
254
+}
255
+
256
+/** FCoE transport interface operations */
257
+static struct interface_operation fcoe_transport_op[] = {
258
+	INTF_OP ( xfer_deliver, struct fcoe_port *, fcoe_deliver ),
259
+	INTF_OP ( xfer_alloc_iob, struct fcoe_port *, fcoe_alloc_iob ),
260
+	INTF_OP ( xfer_window, struct fcoe_port *, fcoe_window ),
261
+	INTF_OP ( intf_close, struct fcoe_port *, fcoe_close ),
262
+};
263
+
264
+/** FCoE transport interface descriptor */
265
+static struct interface_descriptor fcoe_transport_desc =
266
+	INTF_DESC ( struct fcoe_port, transport, fcoe_transport_op );
267
+
268
+/**
269
+ * Create FCoE port
270
+ *
271
+ * @v netdev		Network device
272
+ * @ret rc		Return status code
273
+ */
274
+static int fcoe_probe ( struct net_device *netdev ) {
275
+	struct ll_protocol *ll_protocol = netdev->ll_protocol;
276
+	struct fcoe_port *fcoe;
277
+	union fcoe_name node_wwn;
278
+	union fcoe_name port_wwn;
279
+	int rc;
280
+
281
+	/* Sanity check */
282
+	if ( ll_protocol->ll_proto != htons ( ARPHRD_ETHER ) ) {
283
+		/* Not an error; simply skip this net device */
284
+		DBG ( "FCoE skipping non-Ethernet device %s\n", netdev->name );
285
+		rc = 0;
286
+		goto err_non_ethernet;
287
+	}
288
+	assert ( ll_protocol->ll_addr_len == sizeof ( fcoe->fcf_ll_addr ) );
289
+
290
+	/* Allocate and initialise structure */
291
+	fcoe = zalloc ( sizeof ( *fcoe ) );
292
+	if ( ! fcoe ) {
293
+		rc = -ENOMEM;
294
+		goto err_zalloc;
295
+	}
296
+	ref_init ( &fcoe->refcnt, NULL );
297
+	intf_init ( &fcoe->transport, &fcoe_transport_desc, &fcoe->refcnt );
298
+	fcoe->netdev = netdev_get ( netdev );
299
+
300
+	/* Construct node and port names */
301
+	node_wwn.fcoe.authority = htons ( FCOE_AUTHORITY_IEEE );
302
+	memcpy ( &node_wwn.fcoe.mac, netdev->ll_addr,
303
+		 sizeof ( node_wwn.fcoe.mac ) );
304
+	port_wwn.fcoe.authority = htons ( FCOE_AUTHORITY_IEEE_EXTENDED );
305
+	memcpy ( &port_wwn.fcoe.mac, netdev->ll_addr,
306
+		 sizeof ( port_wwn.fcoe.mac ) );
307
+
308
+	/* Construct initial FCF address */
309
+	memcpy ( &fcoe->fcf_ll_addr, &fcoe_default_fcf_ll_addr,
310
+		 sizeof ( fcoe->fcf_ll_addr ) );
311
+
312
+	DBGC ( fcoe, "FCoE %s is %s", fcoe->netdev->name,
313
+	       fc_ntoa ( &node_wwn.fc ) );
314
+	DBGC ( fcoe, " port %s\n", fc_ntoa ( &port_wwn.fc ) );
315
+
316
+	/* Attach Fibre Channel port */
317
+	if ( ( rc = fc_port_open ( &fcoe->transport, &node_wwn.fc,
318
+				   &port_wwn.fc ) ) != 0 )
319
+		goto err_fc_create;
320
+
321
+	/* Transfer reference to port list */
322
+	list_add ( &fcoe->list, &fcoe_ports );
323
+	return 0;
324
+
325
+ err_fc_create:
326
+	netdev_put ( fcoe->netdev );
327
+ err_zalloc:
328
+ err_non_ethernet:
329
+	return rc;
330
+}
331
+
332
+/**
333
+ * Handle FCoE port device or link state change
334
+ *
335
+ * @v netdev		Network device
336
+ */
337
+static void fcoe_notify ( struct net_device *netdev ) {
338
+	struct fcoe_port *fcoe;
339
+
340
+	/* Sanity check */
341
+	if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) {
342
+		DBG ( "FCoE notification for net device %s missing FCoE "
343
+		      "port\n", netdev->name );
344
+		return;
345
+	}
346
+
347
+	/* Send notification of potential window change */
348
+	xfer_window_changed ( &fcoe->transport );
349
+}
350
+
351
+/**
352
+ * Destroy FCoE port
353
+ *
354
+ * @v netdev		Network device
355
+ */
356
+static void fcoe_remove ( struct net_device *netdev ) {
357
+	struct fcoe_port *fcoe;
358
+
359
+	/* Sanity check */
360
+	if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) {
361
+		DBG ( "FCoE removal of net device %s missing FCoE port\n",
362
+		      netdev->name );
363
+		return;
364
+	}
365
+
366
+	/* Close FCoE device */
367
+	fcoe_close ( fcoe, 0 );
368
+}
369
+
370
+/** FCoE driver */
371
+struct net_driver fcoe_driver __net_driver = {
372
+	.name = "FCoE",
373
+	.probe = fcoe_probe,
374
+	.notify = fcoe_notify,
375
+	.remove = fcoe_remove,
376
+};
377
+
378
+/** FCoE protocol */
379
+struct net_protocol fcoe_protocol __net_protocol = {
380
+	.name = "FCoE",
381
+	.net_proto = htons ( ETH_P_FCOE ),
382
+	.rx = fcoe_rx,
383
+};

Chargement…
Annuler
Enregistrer