source: bootcd/isolinux/syslinux-6.03/gpxe/src/net/netdevice.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 15.4 KB
RevLine 
[e16e8f2]1/*
2 * Copyright (C) 2006 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
19FILE_LICENCE ( GPL2_OR_LATER );
20
21#include <stdint.h>
22#include <stdlib.h>
23#include <stdio.h>
24#include <byteswap.h>
25#include <string.h>
26#include <errno.h>
27#include <gpxe/if_ether.h>
28#include <gpxe/iobuf.h>
29#include <gpxe/tables.h>
30#include <gpxe/process.h>
31#include <gpxe/init.h>
32#include <gpxe/device.h>
33#include <gpxe/errortab.h>
34#include <gpxe/netdevice.h>
35
36/** @file
37 *
38 * Network device management
39 *
40 */
41
42/** List of network devices */
43struct list_head net_devices = LIST_HEAD_INIT ( net_devices );
44
45/** List of open network devices, in reverse order of opening */
46static struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices );
47
48/** Default link status code */
49#define EUNKNOWN_LINK_STATUS EINPROGRESS
50
51/** Human-readable message for the default link status */
52struct errortab netdev_errors[] __errortab = {
53        { EUNKNOWN_LINK_STATUS, "Unknown" },
54};
55
56/**
57 * Mark network device as having link down
58 *
59 * @v netdev            Network device
60 */
61void netdev_link_down ( struct net_device *netdev ) {
62
63        switch ( netdev->link_rc ) {
64        case 0:
65        case -EUNKNOWN_LINK_STATUS:
66                netdev->link_rc = -ENOTCONN;
67                break;
68        default:
69                /* Avoid clobbering a more detailed link status code,
70                 * if one is already set.
71                 */
72                break;
73        }
74}
75
76/**
77 * Record network device statistic
78 *
79 * @v stats             Network device statistics
80 * @v rc                Status code
81 */
82static void netdev_record_stat ( struct net_device_stats *stats, int rc ) {
83        struct net_device_error *error;
84        struct net_device_error *least_common_error;
85        unsigned int i;
86
87        /* If this is not an error, just update the good counter */
88        if ( rc == 0 ) {
89                stats->good++;
90                return;
91        }
92
93        /* Update the bad counter */
94        stats->bad++;
95
96        /* Locate the appropriate error record */
97        least_common_error = &stats->errors[0];
98        for ( i = 0 ; i < ( sizeof ( stats->errors ) /
99                            sizeof ( stats->errors[0] ) ) ; i++ ) {
100                error = &stats->errors[i];
101                /* Update matching record, if found */
102                if ( error->rc == rc ) {
103                        error->count++;
104                        return;
105                }
106                if ( error->count < least_common_error->count )
107                        least_common_error = error;
108        }
109
110        /* Overwrite the least common error record */
111        least_common_error->rc = rc;
112        least_common_error->count = 1;
113}
114
115/**
116 * Transmit raw packet via network device
117 *
118 * @v netdev            Network device
119 * @v iobuf             I/O buffer
120 * @ret rc              Return status code
121 *
122 * Transmits the packet via the specified network device.  This
123 * function takes ownership of the I/O buffer.
124 */
125int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) {
126        int rc;
127
128        DBGC ( netdev, "NETDEV %p transmitting %p (%p+%zx)\n",
129               netdev, iobuf, iobuf->data, iob_len ( iobuf ) );
130
131        list_add_tail ( &iobuf->list, &netdev->tx_queue );
132
133        if ( ! ( netdev->state & NETDEV_OPEN ) ) {
134                rc = -ENETUNREACH;
135                goto err;
136        }
137               
138        if ( ( rc = netdev->op->transmit ( netdev, iobuf ) ) != 0 )
139                goto err;
140
141        return 0;
142
143 err:
144        netdev_tx_complete_err ( netdev, iobuf, rc );
145        return rc;
146}
147
148/**
149 * Complete network transmission
150 *
151 * @v netdev            Network device
152 * @v iobuf             I/O buffer
153 * @v rc                Packet status code
154 *
155 * The packet must currently be in the network device's TX queue.
156 */
157void netdev_tx_complete_err ( struct net_device *netdev,
158                              struct io_buffer *iobuf, int rc ) {
159
160        /* Update statistics counter */
161        netdev_record_stat ( &netdev->tx_stats, rc );
162        if ( rc == 0 ) {
163                DBGC ( netdev, "NETDEV %p transmission %p complete\n",
164                       netdev, iobuf );
165        } else {
166                DBGC ( netdev, "NETDEV %p transmission %p failed: %s\n",
167                       netdev, iobuf, strerror ( rc ) );
168        }
169
170        /* Catch data corruption as early as possible */
171        assert ( iobuf->list.next != NULL );
172        assert ( iobuf->list.prev != NULL );
173
174        /* Dequeue and free I/O buffer */
175        list_del ( &iobuf->list );
176        free_iob ( iobuf );
177}
178
179/**
180 * Complete network transmission
181 *
182 * @v netdev            Network device
183 * @v rc                Packet status code
184 *
185 * Completes the oldest outstanding packet in the TX queue.
186 */
187void netdev_tx_complete_next_err ( struct net_device *netdev, int rc ) {
188        struct io_buffer *iobuf;
189
190        list_for_each_entry ( iobuf, &netdev->tx_queue, list ) {
191                netdev_tx_complete_err ( netdev, iobuf, rc );
192                return;
193        }
194}
195
196/**
197 * Flush device's transmit queue
198 *
199 * @v netdev            Network device
200 */
201static void netdev_tx_flush ( struct net_device *netdev ) {
202
203        /* Discard any packets in the TX queue */
204        while ( ! list_empty ( &netdev->tx_queue ) ) {
205                netdev_tx_complete_next_err ( netdev, -ECANCELED );
206        }
207}
208
209/**
210 * Add packet to receive queue
211 *
212 * @v netdev            Network device
213 * @v iobuf             I/O buffer, or NULL
214 *
215 * The packet is added to the network device's RX queue.  This
216 * function takes ownership of the I/O buffer.
217 */
218void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf ) {
219
220        DBGC ( netdev, "NETDEV %p received %p (%p+%zx)\n",
221               netdev, iobuf, iobuf->data, iob_len ( iobuf ) );
222
223        /* Enqueue packet */
224        list_add_tail ( &iobuf->list, &netdev->rx_queue );
225
226        /* Update statistics counter */
227        netdev_record_stat ( &netdev->rx_stats, 0 );
228}
229
230/**
231 * Discard received packet
232 *
233 * @v netdev            Network device
234 * @v iobuf             I/O buffer, or NULL
235 * @v rc                Packet status code
236 *
237 * The packet is discarded and an RX error is recorded.  This function
238 * takes ownership of the I/O buffer.  @c iobuf may be NULL if, for
239 * example, the net device wishes to report an error due to being
240 * unable to allocate an I/O buffer.
241 */
242void netdev_rx_err ( struct net_device *netdev,
243                     struct io_buffer *iobuf, int rc ) {
244
245        DBGC ( netdev, "NETDEV %p failed to receive %p: %s\n",
246               netdev, iobuf, strerror ( rc ) );
247
248        /* Discard packet */
249        free_iob ( iobuf );
250
251        /* Update statistics counter */
252        netdev_record_stat ( &netdev->rx_stats, rc );
253}
254
255/**
256 * Poll for completed and received packets on network device
257 *
258 * @v netdev            Network device
259 *
260 * Polls the network device for completed transmissions and received
261 * packets.  Any received packets will be added to the RX packet queue
262 * via netdev_rx().
263 */
264void netdev_poll ( struct net_device *netdev ) {
265
266        if ( netdev->state & NETDEV_OPEN )
267                netdev->op->poll ( netdev );
268}
269
270/**
271 * Remove packet from device's receive queue
272 *
273 * @v netdev            Network device
274 * @ret iobuf           I/O buffer, or NULL
275 *
276 * Removes the first packet from the device's RX queue and returns it.
277 * Ownership of the packet is transferred to the caller.
278 */
279struct io_buffer * netdev_rx_dequeue ( struct net_device *netdev ) {
280        struct io_buffer *iobuf;
281
282        list_for_each_entry ( iobuf, &netdev->rx_queue, list ) {
283                list_del ( &iobuf->list );
284                return iobuf;
285        }
286        return NULL;
287}
288
289/**
290 * Flush device's receive queue
291 *
292 * @v netdev            Network device
293 */
294static void netdev_rx_flush ( struct net_device *netdev ) {
295        struct io_buffer *iobuf;
296
297        /* Discard any packets in the RX queue */
298        while ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) {
299                netdev_rx_err ( netdev, iobuf, -ECANCELED );
300        }
301}
302
303/**
304 * Free network device
305 *
306 * @v refcnt            Network device reference counter
307 */
308static void free_netdev ( struct refcnt *refcnt ) {
309        struct net_device *netdev =
310                container_of ( refcnt, struct net_device, refcnt );
311       
312        netdev_tx_flush ( netdev );
313        netdev_rx_flush ( netdev );
314        clear_settings ( netdev_settings ( netdev ) );
315        free ( netdev );
316}
317
318/**
319 * Allocate network device
320 *
321 * @v priv_size         Size of private data area (net_device::priv)
322 * @ret netdev          Network device, or NULL
323 *
324 * Allocates space for a network device and its private data area.
325 */
326struct net_device * alloc_netdev ( size_t priv_size ) {
327        struct net_device *netdev;
328        size_t total_len;
329
330        total_len = ( sizeof ( *netdev ) + priv_size );
331        netdev = zalloc ( total_len );
332        if ( netdev ) {
333                netdev->refcnt.free = free_netdev;
334                netdev->link_rc = -EUNKNOWN_LINK_STATUS;
335                INIT_LIST_HEAD ( &netdev->tx_queue );
336                INIT_LIST_HEAD ( &netdev->rx_queue );
337                netdev_settings_init ( netdev );
338                netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) );
339        }
340        return netdev;
341}
342
343/**
344 * Register network device
345 *
346 * @v netdev            Network device
347 * @ret rc              Return status code
348 *
349 * Gives the network device a name and adds it to the list of network
350 * devices.
351 */
352int register_netdev ( struct net_device *netdev ) {
353        static unsigned int ifindex = 0;
354        int rc;
355
356        /* Create device name */
357        snprintf ( netdev->name, sizeof ( netdev->name ), "net%d",
358                   ifindex++ );
359
360        /* Set initial link-layer address */
361        netdev->ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr );
362
363        /* Register per-netdev configuration settings */
364        if ( ( rc = register_settings ( netdev_settings ( netdev ),
365                                        NULL ) ) != 0 ) {
366                DBGC ( netdev, "NETDEV %p could not register settings: %s\n",
367                       netdev, strerror ( rc ) );
368                return rc;
369        }
370
371        /* Add to device list */
372        netdev_get ( netdev );
373        list_add_tail ( &netdev->list, &net_devices );
374        DBGC ( netdev, "NETDEV %p registered as %s (phys %s hwaddr %s)\n",
375               netdev, netdev->name, netdev->dev->name,
376               netdev_addr ( netdev ) );
377
378        return 0;
379}
380
381/**
382 * Open network device
383 *
384 * @v netdev            Network device
385 * @ret rc              Return status code
386 */
387int netdev_open ( struct net_device *netdev ) {
388        int rc;
389
390        /* Do nothing if device is already open */
391        if ( netdev->state & NETDEV_OPEN )
392                return 0;
393
394        DBGC ( netdev, "NETDEV %p opening\n", netdev );
395
396        /* Open the device */
397        if ( ( rc = netdev->op->open ( netdev ) ) != 0 )
398                return rc;
399
400        /* Mark as opened */
401        netdev->state |= NETDEV_OPEN;
402
403        /* Add to head of open devices list */
404        list_add ( &netdev->open_list, &open_net_devices );
405
406        return 0;
407}
408
409/**
410 * Close network device
411 *
412 * @v netdev            Network device
413 */
414void netdev_close ( struct net_device *netdev ) {
415
416        /* Do nothing if device is already closed */
417        if ( ! ( netdev->state & NETDEV_OPEN ) )
418                return;
419
420        DBGC ( netdev, "NETDEV %p closing\n", netdev );
421
422        /* Close the device */
423        netdev->op->close ( netdev );
424
425        /* Flush TX and RX queues */
426        netdev_tx_flush ( netdev );
427        netdev_rx_flush ( netdev );
428
429        /* Mark as closed */
430        netdev->state &= ~NETDEV_OPEN;
431
432        /* Remove from open devices list */
433        list_del ( &netdev->open_list );
434}
435
436/**
437 * Unregister network device
438 *
439 * @v netdev            Network device
440 *
441 * Removes the network device from the list of network devices.
442 */
443void unregister_netdev ( struct net_device *netdev ) {
444
445        /* Ensure device is closed */
446        netdev_close ( netdev );
447
448        /* Unregister per-netdev configuration settings */
449        unregister_settings ( netdev_settings ( netdev ) );
450
451        /* Remove from device list */
452        list_del ( &netdev->list );
453        netdev_put ( netdev );
454        DBGC ( netdev, "NETDEV %p unregistered\n", netdev );
455}
456
457/** Enable or disable interrupts
458 *
459 * @v netdev            Network device
460 * @v enable            Interrupts should be enabled
461 */
462void netdev_irq ( struct net_device *netdev, int enable ) {
463        netdev->op->irq ( netdev, enable );
464}
465
466/**
467 * Get network device by name
468 *
469 * @v name              Network device name
470 * @ret netdev          Network device, or NULL
471 */
472struct net_device * find_netdev ( const char *name ) {
473        struct net_device *netdev;
474
475        list_for_each_entry ( netdev, &net_devices, list ) {
476                if ( strcmp ( netdev->name, name ) == 0 )
477                        return netdev;
478        }
479
480        return NULL;
481}
482
483/**
484 * Get network device by PCI bus:dev.fn address
485 *
486 * @v bus_type          Bus type
487 * @v location          Bus location
488 * @ret netdev          Network device, or NULL
489 */
490struct net_device * find_netdev_by_location ( unsigned int bus_type,
491                                              unsigned int location ) {
492        struct net_device *netdev;
493
494        list_for_each_entry ( netdev, &net_devices, list ) {
495                if ( ( netdev->dev->desc.bus_type == bus_type ) &&
496                     ( netdev->dev->desc.location == location ) )
497                        return netdev;
498        }
499
500        return NULL;   
501}
502
503/**
504 * Get most recently opened network device
505 *
506 * @ret netdev          Most recently opened network device, or NULL
507 */
508struct net_device * last_opened_netdev ( void ) {
509        struct net_device *netdev;
510
511        list_for_each_entry ( netdev, &open_net_devices, open_list ) {
512                assert ( netdev->state & NETDEV_OPEN );
513                return netdev;
514        }
515
516        return NULL;
517}
518
519/**
520 * Transmit network-layer packet
521 *
522 * @v iobuf             I/O buffer
523 * @v netdev            Network device
524 * @v net_protocol      Network-layer protocol
525 * @v ll_dest           Destination link-layer address
526 * @ret rc              Return status code
527 *
528 * Prepends link-layer headers to the I/O buffer and transmits the
529 * packet via the specified network device.  This function takes
530 * ownership of the I/O buffer.
531 */
532int net_tx ( struct io_buffer *iobuf, struct net_device *netdev,
533             struct net_protocol *net_protocol, const void *ll_dest ) {
534        struct ll_protocol *ll_protocol = netdev->ll_protocol;
535        int rc;
536
537        /* Force a poll on the netdevice to (potentially) clear any
538         * backed-up TX completions.  This is needed on some network
539         * devices to avoid excessive losses due to small TX ring
540         * sizes.
541         */
542        netdev_poll ( netdev );
543
544        /* Add link-layer header */
545        if ( ( rc = ll_protocol->push ( netdev, iobuf, ll_dest, netdev->ll_addr,
546                                        net_protocol->net_proto ) ) != 0 ) {
547                free_iob ( iobuf );
548                return rc;
549        }
550
551        /* Transmit packet */
552        return netdev_tx ( netdev, iobuf );
553}
554
555/**
556 * Process received network-layer packet
557 *
558 * @v iobuf             I/O buffer
559 * @v netdev            Network device
560 * @v net_proto         Network-layer protocol, in network-byte order
561 * @v ll_source         Source link-layer address
562 * @ret rc              Return status code
563 */
564int net_rx ( struct io_buffer *iobuf, struct net_device *netdev,
565             uint16_t net_proto, const void *ll_source ) {
566        struct net_protocol *net_protocol;
567
568        /* Hand off to network-layer protocol, if any */
569        for_each_table_entry ( net_protocol, NET_PROTOCOLS ) {
570                if ( net_protocol->net_proto == net_proto )
571                        return net_protocol->rx ( iobuf, netdev, ll_source );
572        }
573
574        DBGC ( netdev, "NETDEV %p unknown network protocol %04x\n",
575               netdev, ntohs ( net_proto ) );
576        free_iob ( iobuf );
577        return 0;
578}
579
580/**
581 * Single-step the network stack
582 *
583 * @v process           Network stack process
584 *
585 * This polls all interfaces for received packets, and processes
586 * packets from the RX queue.
587 */
588static void net_step ( struct process *process __unused ) {
589        struct net_device *netdev;
590        struct io_buffer *iobuf;
591        struct ll_protocol *ll_protocol;
592        const void *ll_dest;
593        const void *ll_source;
594        uint16_t net_proto;
595        int rc;
596
597        /* Poll and process each network device */
598        list_for_each_entry ( netdev, &net_devices, list ) {
599
600                /* Poll for new packets */
601                netdev_poll ( netdev );
602
603                /* Process at most one received packet.  Give priority
604                 * to getting packets out of the NIC over processing
605                 * the received packets, because we advertise a window
606                 * that assumes that we can receive packets from the
607                 * NIC faster than they arrive.
608                 */
609                if ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) {
610
611                        DBGC ( netdev, "NETDEV %p processing %p (%p+%zx)\n",
612                               netdev, iobuf, iobuf->data,
613                               iob_len ( iobuf ) );
614
615                        /* Remove link-layer header */
616                        ll_protocol = netdev->ll_protocol;
617                        if ( ( rc = ll_protocol->pull ( netdev, iobuf,
618                                                        &ll_dest, &ll_source,
619                                                        &net_proto ) ) != 0 ) {
620                                free_iob ( iobuf );
621                                continue;
622                        }
623
624                        net_rx ( iobuf, netdev, net_proto, ll_source );
625                }
626        }
627}
628
629/** Networking stack process */
630struct process net_process __permanent_process = {
631        .list = LIST_HEAD_INIT ( net_process.list ),
632        .step = net_step,
633};
Note: See TracBrowser for help on using the repository browser.