Annotation of qemu/roms/ipxe/src/drivers/net/virtio-net.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * (c) Copyright 2010 Stefan Hajnoczi <[email protected]>
        !             3:  *
        !             4:  * based on the Etherboot virtio-net driver
        !             5:  *
        !             6:  *  (c) Copyright 2008 Bull S.A.S.
        !             7:  *
        !             8:  *  Author: Laurent Vivier <[email protected]>
        !             9:  *
        !            10:  * some parts from Linux Virtio PCI driver
        !            11:  *
        !            12:  *  Copyright IBM Corp. 2007
        !            13:  *  Authors: Anthony Liguori  <[email protected]>
        !            14:  *
        !            15:  *  some parts from Linux Virtio Ring
        !            16:  *
        !            17:  *  Copyright Rusty Russell IBM Corporation 2007
        !            18:  *
        !            19:  * This work is licensed under the terms of the GNU GPL, version 2 or later.
        !            20:  * See the COPYING file in the top-level directory.
        !            21:  */
        !            22: 
        !            23: FILE_LICENCE ( GPL2_OR_LATER );
        !            24: 
        !            25: #include <errno.h>
        !            26: #include <stdlib.h>
        !            27: #include <ipxe/list.h>
        !            28: #include <ipxe/iobuf.h>
        !            29: #include <ipxe/netdevice.h>
        !            30: #include <ipxe/pci.h>
        !            31: #include <ipxe/if_ether.h>
        !            32: #include <ipxe/ethernet.h>
        !            33: #include <ipxe/virtio-ring.h>
        !            34: #include <ipxe/virtio-pci.h>
        !            35: #include "virtio-net.h"
        !            36: 
        !            37: /*
        !            38:  * Virtio network device driver
        !            39:  *
        !            40:  * Specification:
        !            41:  * http://ozlabs.org/~rusty/virtio-spec/
        !            42:  *
        !            43:  * The virtio network device is supported by Linux virtualization software
        !            44:  * including QEMU/KVM and lguest.  This driver supports the virtio over PCI
        !            45:  * transport; virtual machines have one virtio-net PCI adapter per NIC.
        !            46:  *
        !            47:  * Virtio-net is different from hardware NICs because virtio devices
        !            48:  * communicate with the hypervisor via virtqueues, not traditional descriptor
        !            49:  * rings.  Virtqueues are unordered queues, they support add_buf() and
        !            50:  * get_buf() operations.  To transmit a packet, the driver has to add the
        !            51:  * packet buffer onto the virtqueue.  To receive a packet, the driver must
        !            52:  * first add an empty buffer to the virtqueue and then get the filled packet
        !            53:  * buffer on completion.
        !            54:  *
        !            55:  * Virtqueues are an abstraction that is commonly implemented using the vring
        !            56:  * descriptor ring layout.  The vring is the actual shared memory structure
        !            57:  * that allows the virtual machine to communicate buffers with the hypervisor.
        !            58:  * Because the vring layout is optimized for flexibility and performance rather
        !            59:  * than space, it is heavy-weight and allocated like traditional descriptor
        !            60:  * rings in the open() function of the driver and not in probe().
        !            61:  *
        !            62:  * There is no true interrupt enable/disable.  Virtqueues have callback
        !            63:  * enable/disable flags but these are only hints.  The hypervisor may still
        !            64:  * raise an interrupt.  Nevertheless, this driver disables callbacks in the
        !            65:  * hopes of avoiding interrupts.
        !            66:  */
        !            67: 
        !            68: /* Driver types are declared here so virtio-net.h can be easily synced with its
        !            69:  * Linux source.
        !            70:  */
        !            71: 
        !            72: /* Virtqueue indicies */
        !            73: enum {
        !            74:        RX_INDEX = 0,
        !            75:        TX_INDEX,
        !            76:        QUEUE_NB
        !            77: };
        !            78: 
        !            79: enum {
        !            80:        /** Max number of pending rx packets */
        !            81:        NUM_RX_BUF = 8,
        !            82: 
        !            83:        /** Max Ethernet frame length, including FCS and VLAN tag */
        !            84:        RX_BUF_SIZE = 1522,
        !            85: };
        !            86: 
        !            87: struct virtnet_nic {
        !            88:        /** Base pio register address */
        !            89:        unsigned long ioaddr;
        !            90: 
        !            91:        /** RX/TX virtqueues */
        !            92:        struct vring_virtqueue *virtqueue;
        !            93: 
        !            94:        /** RX packets handed to the NIC waiting to be filled in */
        !            95:        struct list_head rx_iobufs;
        !            96: 
        !            97:        /** Pending rx packet count */
        !            98:        unsigned int rx_num_iobufs;
        !            99: 
        !           100:        /** Virtio net packet header, we only need one */
        !           101:        struct virtio_net_hdr empty_header;
        !           102: };
        !           103: 
        !           104: /** Add an iobuf to a virtqueue
        !           105:  *
        !           106:  * @v netdev           Network device
        !           107:  * @v vq_idx           Virtqueue index (RX_INDEX or TX_INDEX)
        !           108:  * @v iobuf            I/O buffer
        !           109:  *
        !           110:  * The virtqueue is kicked after the iobuf has been added.
        !           111:  */
        !           112: static void virtnet_enqueue_iob ( struct net_device *netdev,
        !           113:                                  int vq_idx, struct io_buffer *iobuf ) {
        !           114:        struct virtnet_nic *virtnet = netdev->priv;
        !           115:        struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx];
        !           116:        unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0;
        !           117:        unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2;
        !           118:        struct vring_list list[] = {
        !           119:                {
        !           120:                        /* Share a single zeroed virtio net header between all
        !           121:                         * rx and tx packets.  This works because this driver
        !           122:                         * does not use any advanced features so none of the
        !           123:                         * header fields get used.
        !           124:                         */
        !           125:                        .addr = ( char* ) &virtnet->empty_header,
        !           126:                        .length = sizeof ( virtnet->empty_header ),
        !           127:                },
        !           128:                {
        !           129:                        .addr = ( char* ) iobuf->data,
        !           130:                        .length = iob_len ( iobuf ),
        !           131:                },
        !           132:        };
        !           133: 
        !           134:        DBGC ( virtnet, "VIRTIO-NET %p enqueuing iobuf %p on vq %d\n",
        !           135:               virtnet, iobuf, vq_idx );
        !           136: 
        !           137:        vring_add_buf ( vq, list, out, in, iobuf, 0 );
        !           138:        vring_kick ( virtnet->ioaddr, vq, 1 );
        !           139: }
        !           140: 
        !           141: /** Try to keep rx virtqueue filled with iobufs
        !           142:  *
        !           143:  * @v netdev           Network device
        !           144:  */
        !           145: static void virtnet_refill_rx_virtqueue ( struct net_device *netdev ) {
        !           146:        struct virtnet_nic *virtnet = netdev->priv;
        !           147: 
        !           148:        while ( virtnet->rx_num_iobufs < NUM_RX_BUF ) {
        !           149:                struct io_buffer *iobuf;
        !           150: 
        !           151:                /* Try to allocate a buffer, stop for now if out of memory */
        !           152:                iobuf = alloc_iob ( RX_BUF_SIZE );
        !           153:                if ( ! iobuf )
        !           154:                        break;
        !           155: 
        !           156:                /* Keep track of iobuf so close() can free it */
        !           157:                list_add ( &iobuf->list, &virtnet->rx_iobufs );
        !           158: 
        !           159:                /* Mark packet length until we know the actual size */
        !           160:                iob_put ( iobuf, RX_BUF_SIZE );
        !           161: 
        !           162:                virtnet_enqueue_iob ( netdev, RX_INDEX, iobuf );
        !           163:                virtnet->rx_num_iobufs++;
        !           164:        }
        !           165: }
        !           166: 
        !           167: /** Open network device
        !           168:  *
        !           169:  * @v netdev   Network device
        !           170:  * @ret rc     Return status code
        !           171:  */
        !           172: static int virtnet_open ( struct net_device *netdev ) {
        !           173:        struct virtnet_nic *virtnet = netdev->priv;
        !           174:        unsigned long ioaddr = virtnet->ioaddr;
        !           175:        u32 features;
        !           176:        int i;
        !           177: 
        !           178:        /* Reset for sanity */
        !           179:        vp_reset ( ioaddr );
        !           180: 
        !           181:        /* Allocate virtqueues */
        !           182:        virtnet->virtqueue = zalloc ( QUEUE_NB *
        !           183:                                      sizeof ( *virtnet->virtqueue ) );
        !           184:        if ( ! virtnet->virtqueue )
        !           185:                return -ENOMEM;
        !           186: 
        !           187:        /* Initialize rx/tx virtqueues */
        !           188:        for ( i = 0; i < QUEUE_NB; i++ ) {
        !           189:                if ( vp_find_vq ( ioaddr, i, &virtnet->virtqueue[i] ) == -1 ) {
        !           190:                        DBGC ( virtnet, "VIRTIO-NET %p cannot register queue %d\n",
        !           191:                               virtnet, i );
        !           192:                        free ( virtnet->virtqueue );
        !           193:                        virtnet->virtqueue = NULL;
        !           194:                        return -ENOENT;
        !           195:                }
        !           196:        }
        !           197: 
        !           198:        /* Initialize rx packets */
        !           199:        INIT_LIST_HEAD ( &virtnet->rx_iobufs );
        !           200:        virtnet->rx_num_iobufs = 0;
        !           201:        virtnet_refill_rx_virtqueue ( netdev );
        !           202: 
        !           203:        /* Disable interrupts before starting */
        !           204:        netdev_irq ( netdev, 0 );
        !           205: 
        !           206:        /* Driver is ready */
        !           207:        features = vp_get_features ( ioaddr );
        !           208:        vp_set_features ( ioaddr, features & ( 1 << VIRTIO_NET_F_MAC ) );
        !           209:        vp_set_status ( ioaddr, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK );
        !           210:        return 0;
        !           211: }
        !           212: 
        !           213: /** Close network device
        !           214:  *
        !           215:  * @v netdev   Network device
        !           216:  */
        !           217: static void virtnet_close ( struct net_device *netdev ) {
        !           218:        struct virtnet_nic *virtnet = netdev->priv;
        !           219:        struct io_buffer *iobuf;
        !           220:        struct io_buffer *next_iobuf;
        !           221: 
        !           222:        vp_reset ( virtnet->ioaddr );
        !           223: 
        !           224:        /* Virtqueues can be freed now that NIC is reset */
        !           225:        free ( virtnet->virtqueue );
        !           226:        virtnet->virtqueue = NULL;
        !           227: 
        !           228:        /* Free rx iobufs */
        !           229:        list_for_each_entry_safe ( iobuf, next_iobuf, &virtnet->rx_iobufs, list ) {
        !           230:                free_iob ( iobuf );
        !           231:        }
        !           232:        INIT_LIST_HEAD ( &virtnet->rx_iobufs );
        !           233:        virtnet->rx_num_iobufs = 0;
        !           234: }
        !           235: 
        !           236: /** Transmit packet
        !           237:  *
        !           238:  * @v netdev   Network device
        !           239:  * @v iobuf    I/O buffer
        !           240:  * @ret rc     Return status code
        !           241:  */
        !           242: static int virtnet_transmit ( struct net_device *netdev,
        !           243:                              struct io_buffer *iobuf ) {
        !           244:        virtnet_enqueue_iob ( netdev, TX_INDEX, iobuf );
        !           245:        return 0;
        !           246: }
        !           247: 
        !           248: /** Complete packet transmission
        !           249:  *
        !           250:  * @v netdev   Network device
        !           251:  */
        !           252: static void virtnet_process_tx_packets ( struct net_device *netdev ) {
        !           253:        struct virtnet_nic *virtnet = netdev->priv;
        !           254:        struct vring_virtqueue *tx_vq = &virtnet->virtqueue[TX_INDEX];
        !           255: 
        !           256:        while ( vring_more_used ( tx_vq ) ) {
        !           257:                struct io_buffer *iobuf = vring_get_buf ( tx_vq, NULL );
        !           258: 
        !           259:                DBGC ( virtnet, "VIRTIO-NET %p tx complete iobuf %p\n",
        !           260:                       virtnet, iobuf );
        !           261: 
        !           262:                netdev_tx_complete ( netdev, iobuf );
        !           263:        }
        !           264: }
        !           265: 
        !           266: /** Complete packet reception
        !           267:  *
        !           268:  * @v netdev   Network device
        !           269:  */
        !           270: static void virtnet_process_rx_packets ( struct net_device *netdev ) {
        !           271:        struct virtnet_nic *virtnet = netdev->priv;
        !           272:        struct vring_virtqueue *rx_vq = &virtnet->virtqueue[RX_INDEX];
        !           273: 
        !           274:        while ( vring_more_used ( rx_vq ) ) {
        !           275:                unsigned int len;
        !           276:                struct io_buffer *iobuf = vring_get_buf ( rx_vq, &len );
        !           277: 
        !           278:                /* Release ownership of iobuf */
        !           279:                list_del ( &iobuf->list );
        !           280:                virtnet->rx_num_iobufs--;
        !           281: 
        !           282:                /* Update iobuf length */
        !           283:                iob_unput ( iobuf, RX_BUF_SIZE );
        !           284:                iob_put ( iobuf, len - sizeof ( struct virtio_net_hdr ) );
        !           285: 
        !           286:                DBGC ( virtnet, "VIRTIO-NET %p rx complete iobuf %p len %zd\n",
        !           287:                       virtnet, iobuf, iob_len ( iobuf ) );
        !           288: 
        !           289:                /* Pass completed packet to the network stack */
        !           290:                netdev_rx ( netdev, iobuf );
        !           291:        }
        !           292: 
        !           293:        virtnet_refill_rx_virtqueue ( netdev );
        !           294: }
        !           295: 
        !           296: /** Poll for completed and received packets
        !           297:  *
        !           298:  * @v netdev   Network device
        !           299:  */
        !           300: static void virtnet_poll ( struct net_device *netdev ) {
        !           301:        struct virtnet_nic *virtnet = netdev->priv;
        !           302: 
        !           303:        /* Acknowledge interrupt.  This is necessary for UNDI operation and
        !           304:         * interrupts that are raised despite VRING_AVAIL_F_NO_INTERRUPT being
        !           305:         * set (that flag is just a hint and the hypervisor not not have to
        !           306:         * honor it).
        !           307:         */
        !           308:        vp_get_isr ( virtnet->ioaddr );
        !           309: 
        !           310:        virtnet_process_tx_packets ( netdev );
        !           311:        virtnet_process_rx_packets ( netdev );
        !           312: }
        !           313: 
        !           314: /** Enable or disable interrupts
        !           315:  *
        !           316:  * @v netdev   Network device
        !           317:  * @v enable   Interrupts should be enabled
        !           318:  */
        !           319: static void virtnet_irq ( struct net_device *netdev, int enable ) {
        !           320:        struct virtnet_nic *virtnet = netdev->priv;
        !           321:        int i;
        !           322: 
        !           323:        for ( i = 0; i < QUEUE_NB; i++ ) {
        !           324:                if ( enable )
        !           325:                        vring_enable_cb ( &virtnet->virtqueue[i] );
        !           326:                else
        !           327:                        vring_disable_cb ( &virtnet->virtqueue[i] );
        !           328:        }
        !           329: }
        !           330: 
        !           331: /** virtio-net device operations */
        !           332: static struct net_device_operations virtnet_operations = {
        !           333:        .open = virtnet_open,
        !           334:        .close = virtnet_close,
        !           335:        .transmit = virtnet_transmit,
        !           336:        .poll = virtnet_poll,
        !           337:        .irq = virtnet_irq,
        !           338: };
        !           339: 
        !           340: /**
        !           341:  * Probe PCI device
        !           342:  *
        !           343:  * @v pci      PCI device
        !           344:  * @v id       PCI ID
        !           345:  * @ret rc     Return status code
        !           346:  */
        !           347: static int virtnet_probe ( struct pci_device *pci ) {
        !           348:        unsigned long ioaddr = pci->ioaddr;
        !           349:        struct net_device *netdev;
        !           350:        struct virtnet_nic *virtnet;
        !           351:        u32 features;
        !           352:        int rc;
        !           353: 
        !           354:        /* Allocate and hook up net device */
        !           355:        netdev = alloc_etherdev ( sizeof ( *virtnet ) );
        !           356:        if ( ! netdev )
        !           357:                return -ENOMEM;
        !           358:        netdev_init ( netdev, &virtnet_operations );
        !           359:        virtnet = netdev->priv;
        !           360:        virtnet->ioaddr = ioaddr;
        !           361:        pci_set_drvdata ( pci, netdev );
        !           362:        netdev->dev = &pci->dev;
        !           363: 
        !           364:        DBGC ( virtnet, "VIRTIO-NET %p busaddr=%s ioaddr=%#lx irq=%d\n",
        !           365:               virtnet, pci->dev.name, ioaddr, pci->irq );
        !           366: 
        !           367:        /* Enable PCI bus master and reset NIC */
        !           368:        adjust_pci_device ( pci );
        !           369:        vp_reset ( ioaddr );
        !           370: 
        !           371:        /* Load MAC address */
        !           372:        features = vp_get_features ( ioaddr );
        !           373:        if ( features & ( 1 << VIRTIO_NET_F_MAC ) ) {
        !           374:                vp_get ( ioaddr, offsetof ( struct virtio_net_config, mac ),
        !           375:                         netdev->hw_addr, ETH_ALEN );
        !           376:                DBGC ( virtnet, "VIRTIO-NET %p mac=%s\n", virtnet,
        !           377:                       eth_ntoa ( netdev->hw_addr ) );
        !           378:        }
        !           379: 
        !           380:        /* Register network device */
        !           381:        if ( ( rc = register_netdev ( netdev ) ) != 0 )
        !           382:                goto err_register_netdev;
        !           383: 
        !           384:        /* Mark link as up, control virtqueue is not used */
        !           385:        netdev_link_up ( netdev );
        !           386: 
        !           387:        return 0;
        !           388: 
        !           389:        unregister_netdev ( netdev );
        !           390:  err_register_netdev:
        !           391:        vp_reset ( ioaddr );
        !           392:        netdev_nullify ( netdev );
        !           393:        netdev_put ( netdev );
        !           394:        return rc;
        !           395: }
        !           396: 
        !           397: /**
        !           398:  * Remove device
        !           399:  *
        !           400:  * @v pci      PCI device
        !           401:  */
        !           402: static void virtnet_remove ( struct pci_device *pci ) {
        !           403:        struct net_device *netdev = pci_get_drvdata ( pci );
        !           404: 
        !           405:        unregister_netdev ( netdev );
        !           406:        netdev_nullify ( netdev );
        !           407:        netdev_put ( netdev );
        !           408: }
        !           409: 
        !           410: static struct pci_device_id virtnet_nics[] = {
        !           411: PCI_ROM(0x1af4, 0x1000, "virtio-net", "Virtio Network Interface", 0),
        !           412: };
        !           413: 
        !           414: struct pci_driver virtnet_driver __pci_driver = {
        !           415:        .ids = virtnet_nics,
        !           416:        .id_count = ( sizeof ( virtnet_nics ) / sizeof ( virtnet_nics[0] ) ),
        !           417:        .probe = virtnet_probe,
        !           418:        .remove = virtnet_remove,
        !           419: };

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.