/* * Copyright (C) 2014 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * You can also choose to distribute this program under the terms of * the Unmodified Binary Distribution Licence (as given in the file * COPYING.UBDL), provided that you have satisfied its requirements. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netfront.h" /** @file * * Xen netfront driver * */ /* Disambiguate the various error causes */ #define EIO_NETIF_RSP_ERROR \ __einfo_error ( EINFO_EIO_NETIF_RSP_ERROR ) #define EINFO_EIO_NETIF_RSP_ERROR \ __einfo_uniqify ( EINFO_EIO, -NETIF_RSP_ERROR, \ "Unspecified network error" ) #define EIO_NETIF_RSP_DROPPED \ __einfo_error ( EINFO_EIO_NETIF_RSP_DROPPED ) #define EINFO_EIO_NETIF_RSP_DROPPED \ __einfo_uniqify ( EINFO_EIO, -NETIF_RSP_DROPPED, \ "Packet dropped" ) #define EIO_NETIF_RSP( status ) \ EUNIQ ( EINFO_EIO, ( -(status) & 0x1f ), \ EIO_NETIF_RSP_ERROR, EIO_NETIF_RSP_DROPPED ) /** List of netfront devices */ static LIST_HEAD ( netfront_devices ); /****************************************************************************** * * XenStore interface * ****************************************************************************** */ /** * Reset device * * @v netfront Netfront device * @ret rc Return status code */ static int netfront_reset ( struct netfront_nic *netfront ) { struct xen_device *xendev = netfront->xendev; int state; int rc; /* Get current backend state */ if ( ( state = xenbus_backend_state ( xendev ) ) < 0 ) { rc = state; DBGC ( netfront, "NETFRONT %s could not read backend state: " "%s\n", xendev->key, strerror ( rc ) ); return rc; } /* If the backend is not already in InitWait, then mark * frontend as Closed to shut down the backend. */ if ( state != XenbusStateInitWait ) { /* Set state to Closed */ xenbus_set_state ( xendev, XenbusStateClosed ); /* Wait for backend to reach Closed */ if ( ( rc = xenbus_backend_wait ( xendev, XenbusStateClosed ) ) != 0 ) { DBGC ( netfront, "NETFRONT %s backend did not reach " "Closed: %s\n", xendev->key, strerror ( rc ) ); return rc; } } /* Reset state to Initialising */ xenbus_set_state ( xendev, XenbusStateInitialising ); /* Wait for backend to reach InitWait */ if ( ( rc = xenbus_backend_wait ( xendev, XenbusStateInitWait ) ) != 0){ DBGC ( netfront, "NETFRONT %s backend did not reach InitWait: " "%s\n", xendev->key, strerror ( rc ) ); return rc; } return 0; } /** * Fetch MAC address * * @v netfront Netfront device * @v hw_addr Hardware address to fill in * @ret rc Return status code */ static int netfront_read_mac ( struct netfront_nic *netfront, void *hw_addr ) { struct xen_device *xendev = netfront->xendev; struct xen_hypervisor *xen = xendev->xen; char *mac; int len; int rc; /* Fetch MAC address */ if ( ( rc = xenstore_read ( xen, &mac, xendev->key, "mac", NULL ) )!=0){ DBGC ( netfront, "NETFRONT %s could not read MAC address: %s\n", xendev->key, strerror ( rc ) ); goto err_xenstore_read; } DBGC2 ( netfront, "NETFRONT %s has MAC address \"%s\"\n", xendev->key, mac ); /* Decode MAC address */ len = hex_decode ( ':', mac, hw_addr, ETH_ALEN ); if ( len < 0 ) { rc = len; DBGC ( netfront, "NETFRONT %s could not decode MAC address " "\"%s\": %s\n", xendev->key, mac, strerror ( rc ) ); goto err_decode; } /* Success */ rc = 0; err_decode: free ( mac ); err_xenstore_read: return rc; } /** * Write XenStore numeric value * * @v netfront Netfront device * @v subkey Subkey * @v num Numeric value * @ret rc Return status code */ static int netfront_write_num ( struct netfront_nic *netfront, const char *subkey, unsigned long num ) { struct xen_device *xendev = netfront->xendev; struct xen_hypervisor *xen = xendev->xen; int rc; /* Write value */ if ( ( rc = xenstore_write_num ( xen, num, xendev->key, subkey, NULL ) ) != 0 ) { DBGC ( netfront, "NETFRONT %s could not set %s=\"%ld\": %s\n", xendev->key, subkey, num, strerror ( rc ) ); return rc; } return 0; } /** * Write XenStore flag value * * @v netfront Netfront device * @v subkey Subkey * @v num Numeric value * @ret rc Return status code */ static int netfront_write_flag ( struct netfront_nic *netfront, const char *subkey ) { return netfront_write_num ( netfront, subkey, 1 ); } /** * Delete XenStore value * * @v netfront Netfront device * @v subkey Subkey * @ret rc Return status code */ static int netfront_rm ( struct netfront_nic *netfront, const char *subkey ) { struct xen_device *xendev = netfront->xendev; struct xen_hypervisor *xen = xendev->xen; int rc; /* Remove value */ if ( ( rc = xenstore_rm ( xen, xendev->key, subkey, NULL ) ) != 0 ) { DBGC ( netfront, "NETFRONT %s could not delete %s: %s\n", xendev->key, subkey, strerror ( rc ) ); return rc; } return 0; } /****************************************************************************** * * Events * ****************************************************************************** */ /** * Create event channel * * @v netfront Netfront device * @ret rc Return status code */ static int netfront_create_event ( struct netfront_nic *netfront ) { struct xen_device *xendev = netfront->xendev; struct xen_hypervisor *xen = xendev->xen; struct evtchn_alloc_unbound alloc_unbound; struct evtchn_close close; int xenrc; int rc; /* Allocate event */ alloc_unbound.dom = DOMID_SELF; alloc_unbound.remote_dom = xendev->backend_id; if ( ( xenrc = xenevent_alloc_unbound ( xen, &alloc_unbound ) ) != 0 ) { rc = -EXEN ( xenrc ); DBGC ( netfront, "NETFRONT %s could not allocate event: %s\n", xendev->key, strerror ( rc ) ); goto err_alloc_unbound; } netfront->event.port = alloc_unbound.port; /* Publish event channel */ if ( ( rc = netfront_write_num ( netfront, "event-channel", netfront->event.port ) ) != 0 ) goto err_write_num; DBGC ( netfront, "NETFRONT %s event-channel=\"%d\"\n", xendev->key, netfront->event.port ); return 0; netfront_rm ( netfront, "event-channel" ); err_write_num: close.port = netfront->event.port; xenevent_close ( xen, &close ); err_alloc_unbound: return rc; } /** * Send event * * @v netfront Netfront device * @ret rc Return status code */ static inline __attribute__ (( always_inline )) int netfront_send_event ( struct netfront_nic *netfront ) { struct xen_device *xendev = netfront->xendev; struct xen_hypervisor *xen = xendev->xen; int xenrc; int rc; /* Send event */ if ( ( xenrc = xenevent_send ( xen, &netfront->event ) ) != 0 ) { rc = -EXEN ( xenrc ); DBGC ( netfront, "NETFRONT %s could not send event: %s\n", xendev->key, strerror ( rc ) ); return rc; } return 0; } /** * Destroy event channel * * @v netfront Netfront device */ static void netfront_destroy_event ( struct netfront_nic *netfront ) { struct xen_device *xendev = netfront->xendev; struct xen_hypervisor *xen = xendev->xen; struct evtchn_close close; /* Unpublish event channel */ netfront_rm ( netfront, "event-channel" ); /* Close event channel */ close.port = netfront->event.port; xenevent_close ( xen, &close ); } /****************************************************************************** * * Descriptor rings * ****************************************************************************** */ /** * Create descriptor ring * * @v netfront Netfront device * @v ring Descriptor ring * @ret rc Return status code */ static int netfront_create_ring ( struct netfront_nic *netfront, struct netfront_ring *ring ) { struct xen_device *xendev = netfront->xendev; struct xen_hypervisor *xen = xendev->xen; physaddr_t addr; unsigned int i; int rc; /* Initialise buffer ID ring */ for ( i = 0 ; i < ring->count ; i++ ) { ring->ids[i] = i; assert ( ring->iobufs[i] == NULL ); } ring->id_prod = 0; ring->id_cons = 0; /* Allocate and initialise shared ring */ ring->sring.raw = malloc_phys ( PAGE_SIZE, PAGE_SIZE ); if ( ! ring->sring.raw ) { rc = -ENOMEM; goto err_alloc; } /* Grant access to shared ring */ addr = virt_to_phys ( ring->sring.raw ); if ( ( rc = xengrant_permit_access ( xen, ring->ref, xendev->backend_id, 0, addr ) ) != 0 ) { DBGC ( netfront, "NETFRONT %s could not permit access to " "%#08lx: %s\n", xendev->key, addr, strerror ( rc ) ); goto err_permit_access; } /* Publish shared ring reference */ if ( ( rc = netfront_write_num ( netfront, ring->ref_key, ring->ref ) ) != 0 ) goto err_write_num; DBGC ( netfront, "NETFRONT %s %s=\"%d\" [%08lx,%08lx)\n", xendev->key, ring->ref_key, ring->ref, addr, ( addr + PAGE_SIZE ) ); return 0; netfront_rm ( netfront, ring->ref_key ); err_write_num: xengrant_invalidate ( xen, ring->ref ); err_permit_access: free_phys ( ring->sring.raw, PAGE_SIZE ); err_alloc: return rc; } /** * Add buffer to descriptor ring * * @v netfront Netfront device * @v ring Descriptor ring * @v addr Physical address * @v iobuf Associated I/O buffer, or NULL * @v id Buffer ID to fill in * @v ref Grant reference to fill in * @ret rc Return status code * * The caller is responsible for ensuring that there is space in the * ring. */ static int netfront_push ( struct netfront_nic *netfront, struct netfront_ring *ring, physaddr_t addr, struct io_buffer *iobuf, uint16_t *id, grant_ref_t *ref ) { struct xen_device *xendev = netfront->xendev; struct xen_hypervisor *xen = xendev->xen; unsigned int next_id; unsigned int next_ref; int rc; /* Sanity check */ assert ( ! netfront_ring_is_full ( ring ) ); /* Allocate buffer ID */ next_id = ring->ids[ ring->id_prod & ( ring->count - 1 ) ]; next_ref = ring->refs[next_id]; /* Grant access to page containing address */ if ( ( rc = xengrant_permit_access ( xen, next_ref, xendev->backend_id, 0, addr ) ) != 0 ) { DBGC ( netfront, "NETFRONT %s could not permit access to " "%#08lx: %s\n", xendev->key, addr, strerror ( rc ) ); return rc; } /* Store associated I/O buffer, if any */ assert ( ring->iobufs[next_id] == NULL ); ring->iobufs[next_id] = iobuf; /* Consume buffer ID */ ring->id_prod++; /* Return buffer ID and grant reference */ *id = next_id; *ref = next_ref; return 0; } /** * Remove buffer from descriptor ring * * @v netfront Netfront device * @v ring Descriptor ring * @v id Buffer ID * @ret iobuf Associated I/O buffer, if any */ static struct io_buffer * netfront_pull ( struct netfront_nic *netfront, struct netfront_ring *ring, unsigned int id ) { struct xen_device *xendev = netfront->xendev; struct xen_hypervisor *xen = xendev->xen; struct io_buffer *iobuf; /* Sanity check */ assert ( id < ring->count ); /* Revoke access from I/O buffer page */ xengrant_invalidate ( xen, ring->refs[id] ); /* Retrieve I/O buffer */ iobuf = ring->iobufs[id]; ring->iobufs[id] = NULL; /* Free buffer ID */ ring->ids[ ( ring->id_cons++ ) & ( ring->count - 1 ) ] = id; return iobuf; } /** * Destroy descriptor ring * * @v netfront Netfront device * @v ring Descriptor ring * @v discard Method used to discard outstanding buffer, or NULL */ static void netfront_destroy_ring ( struct netfront_nic *netfront, struct netfront_ring *ring, void ( * discard ) ( struct io_buffer * ) ){ struct xen_device *xendev = netfront->xendev; struct xen_hypervisor *xen = xendev->xen; struct io_buffer *iobuf; unsigned int id; /* Flush any outstanding buffers */ while ( ! netfront_ring_is_empty ( ring ) ) { id = ring->ids[ ring->id_cons & ( ring->count - 1 ) ]; iobuf = netfront_pull ( netfront, ring, id ); if ( discard ) discard ( iobuf ); } /* Unpublish shared ring reference */ netfront_rm ( netfront, ring->ref_key ); /* Revoke access from shared ring */ xengrant_invalidate ( xen, ring->ref ); /* Free page */ free_phys ( ring->sring.raw, PAGE_SIZE ); ring->sring.raw = NULL; } /** * Discard partially received I/O buffers * * @v netfront Netfront device */ static void netfront_discard ( struct netfront_nic *netfront ) { struct io_buffer *iobuf; struct io_buffer *tmp; /* Discard all buffers in the list */ list_for_each_entry_safe ( iobuf, tmp, &netfront->rx_partial, list ) { list_del ( &iobuf->list ); free_iob ( iobuf ); } } /****************************************************************************** * * Network device interface * ****************************************************************************** */ /** * Refill receive descriptor ring * * @v netdev Network device */ static void netfront_refill_rx ( struct net_device *netdev ) { struct netfront_nic *netfront = netdev->priv; struct xen_device *xendev = netfront->xendev; struct io_buffer *iobuf; struct netif_rx_request *request; unsigned int refilled = 0; physaddr_t addr; int notify; int rc; /* Refill ring */ while ( netfront_ring_fill ( &netfront->rx ) < NETFRONT_RX_FILL ) { /* Allocate I/O buffer */ iobuf = alloc_iob ( PAGE_SIZE ); if ( ! iobuf ) { /* Wait for next refill */ break; } addr = virt_to_phys ( iobuf->data ); /* Add to descriptor ring */ request = RING_GET_REQUEST ( &netfront->rx_fring, netfront->rx_fring.req_prod_pvt ); if ( ( rc = netfront_push ( netfront, &netfront->rx, addr, iobuf, &request->id, &request->gref ) ) != 0 ) { netdev_rx_err ( netdev, iobuf, rc ); break; } DBGC2 ( netfront, "NETFRONT %s RX id %d ref %d is %#08lx+%zx\n", xendev->key, request->id, request->gref, addr, iob_tailroom ( iobuf ) ); /* Move to next descriptor */ netfront->rx_fring.req_prod_pvt++; refilled++; } /* Push new descriptors and notify backend if applicable */ if ( refilled ) { RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->rx_fring, notify ); if ( notify ) netfront_send_event ( netfront ); } } /** * Open network device * * @v netdev Network device * @ret rc Return status code */ static int netfront_open ( struct net_device *netdev ) { struct netfront_nic *netfront = netdev->priv; struct xen_device *xendev = netfront->xendev; int rc; /* Ensure device is in a suitable initial state */ if ( ( rc = netfront_reset ( netfront ) ) != 0 ) goto err_reset; /* Create transmit descriptor ring */ if ( ( rc = netfront_create_ring ( netfront, &netfront->tx ) ) != 0 ) goto err_create_tx; SHARED_RING_INIT ( netfront->tx_sring ); FRONT_RING_INIT ( &netfront->tx_fring, netfront->tx_sring, PAGE_SIZE ); assert ( RING_SIZE ( &netfront->tx_fring ) >= netfront->tx.count ); /* Create receive descriptor ring */ if ( ( rc = netfront_create_ring ( netfront, &netfront->rx ) ) != 0 ) goto err_create_rx; SHARED_RING_INIT ( netfront->rx_sring ); FRONT_RING_INIT ( &netfront->rx_fring, netfront->rx_sring, PAGE_SIZE ); assert ( RING_SIZE ( &netfront->rx_fring ) >= netfront->rx.count ); /* Create event channel */ if ( ( rc = netfront_create_event ( netfront ) ) != 0 ) goto err_create_event; /* "Request" the rx-copy feature. Current versions of * xen_netback.ko will fail silently if this parameter is not * present. */ if ( ( rc = netfront_write_flag ( netfront, "request-rx-copy" ) ) != 0 ) goto err_request_rx_copy; /* Inform backend that we can support scatter-gather */ if ( ( rc = netfront_write_flag ( netfront, "feature-sg" ) ) != 0 ) goto err_feature_sg; /* Disable checksum offload, since we will always do the work anyway */ if ( ( rc = netfront_write_flag ( netfront, "feature-no-csum-offload" ) ) != 0 ) goto err_feature_no_csum_offload; /* Inform backend that we will send notifications for RX requests */ if ( ( rc = netfront_write_flag ( netfront, "feature-rx-notify" ) ) != 0 ) goto err_feature_rx_notify; /* Set state to Connected */ if ( ( rc = xenbus_set_state ( xendev, XenbusStateConnected ) ) != 0 ) { DBGC ( netfront, "NETFRONT %s could not set state=\"%d\": %s\n", xendev->key, XenbusStateConnected, strerror ( rc ) ); goto err_set_state; } /* Wait for backend to connect */ if ( ( rc = xenbus_backend_wait ( xendev, XenbusStateConnected ) ) !=0){ DBGC ( netfront, "NETFRONT %s could not connect to backend: " "%s\n", xendev->key, strerror ( rc ) ); goto err_backend_wait; } /* Refill receive descriptor ring */ netfront_refill_rx ( netdev ); /* Set link up */ netdev_link_up ( netdev ); return 0; err_backend_wait: netfront_reset ( netfront ); err_set_state: netfront_rm ( netfront, "feature-rx-notify" ); err_feature_rx_notify: netfront_rm ( netfront, "feature-no-csum-offload" ); err_feature_no_csum_offload: netfront_rm ( netfront, "feature-sg" ); err_feature_sg: netfront_rm ( netfront, "request-rx-copy" ); err_request_rx_copy: netfront_destroy_event ( netfront ); err_create_event: netfront_destroy_ring ( netfront, &netfront->rx, NULL ); err_create_rx: netfront_destroy_ring ( netfront, &netfront->tx, NULL ); err_create_tx: err_reset: return rc; } /** * Close network device * * @v netdev Network device */ static void netfront_close ( struct net_device *netdev ) { struct netfront_nic *netfront = netdev->priv; struct xen_device *xendev = netfront->xendev; int rc; /* Reset devic, thereby ensuring that grant references are no * longer in use, etc. */ if ( ( rc = netfront_reset ( netfront ) ) != 0 ) { DBGC ( netfront, "NETFRONT %s could not disconnect from " "backend: %s\n", xendev->key, strerror ( rc ) ); /* Things will probably go _very_ badly wrong if this * happens, since it means the backend may still write * to the outstanding RX buffers that we are about to * free. The best we can do is report the error via * the link status, but there's a good chance the * machine will crash soon. */ netdev_link_err ( netdev, rc ); } else { netdev_link_down ( netdev ); } /* Delete flags */ netfront_rm ( netfront, "feature-rx-notify" ); netfront_rm ( netfront, "feature-no-csum-offload" ); netfront_rm ( netfront, "feature-sg" ); netfront_rm ( netfront, "request-rx-copy" ); /* Destroy event channel */ netfront_destroy_event ( netfront ); /* Discard any partially received I/O buffers */ netfront_discard ( netfront ); /* Destroy receive descriptor ring, freeing any outstanding * I/O buffers. */ netfront_destroy_ring ( netfront, &netfront->rx, free_iob ); /* Destroy transmit descriptor ring. Leave any outstanding * I/O buffers to be freed by netdev_tx_flush(). */ netfront_destroy_ring ( netfront, &netfront->tx, NULL ); } /** * Transmit packet * * @v netdev Network device * @v iobuf I/O buffer * @ret rc Return status code */ static int netfront_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct netfront_nic *netfront = netdev->priv; struct xen_device *xendev = netfront->xendev; struct netif_tx_request *request; physaddr_t addr; size_t len; size_t remaining; size_t frag_len; unsigned int offset; unsigned int count; unsigned int more; int notify; int rc; /* Calculate number of page buffers required */ addr = virt_to_phys ( iobuf->data ); len = iob_len ( iobuf ); offset = ( addr & ( PAGE_SIZE - 1 ) ); count = ( ( offset + len + PAGE_SIZE - 1 ) / PAGE_SIZE ); /* Check that we have space in the ring */ if ( netfront_ring_space ( &netfront->tx ) < count ) { DBGC ( netfront, "NETFRONT %s out of transmit descriptors\n", xendev->key ); return -ENOBUFS; } /* Add to descriptor ring */ remaining = len; while ( remaining ) { /* Calculate length of this fragment */ frag_len = ( PAGE_SIZE - offset ); if ( frag_len >= remaining ) { frag_len = remaining; more = 0; } else { more = NETTXF_more_data; } /* Populate request */ request = RING_GET_REQUEST ( &netfront->tx_fring, netfront->tx_fring.req_prod_pvt ); if ( ( rc = netfront_push ( netfront, &netfront->tx, addr, ( more ? NULL : iobuf ), &request->id, &request->gref ) ) != 0 ) { return rc; } request->flags = ( NETTXF_data_validated | more ); request->offset = offset; request->size = ( ( remaining == len ) ? len : frag_len ); DBGC2 ( netfront, "NETFRONT %s TX id %d ref %d is " "%#08lx+%zx%s\n", xendev->key, request->id, request->gref, addr, frag_len, ( more ? "..." : "" ) ); /* Move to next descriptor */ netfront->tx_fring.req_prod_pvt++; addr += frag_len; remaining -= frag_len; offset = 0; } /* Push new descriptors and notify backend if applicable */ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->tx_fring, notify ); if ( notify ) netfront_send_event ( netfront ); return 0; } /** * Poll for completed packets * * @v netdev Network device */ static void netfront_poll_tx ( struct net_device *netdev ) { struct netfront_nic *netfront = netdev->priv; struct xen_device *xendev = netfront->xendev; struct netif_tx_response *response; struct io_buffer *iobuf; int status; int rc; /* Consume any unconsumed responses */ while ( RING_HAS_UNCONSUMED_RESPONSES ( &netfront->tx_fring ) ) { /* Get next response */ response = RING_GET_RESPONSE ( &netfront->tx_fring, netfront->tx_fring.rsp_cons++ ); /* Retrieve from descriptor ring */ iobuf = netfront_pull ( netfront, &netfront->tx, response->id ); status = response->status; if ( status >= NETIF_RSP_OKAY ) { DBGC2 ( netfront, "NETFRONT %s TX id %d complete\n", xendev->key, response->id ); if ( iobuf ) netdev_tx_complete ( netdev, iobuf ); } else { rc = -EIO_NETIF_RSP ( status ); DBGC2 ( netfront, "NETFRONT %s TX id %d error %d: %s\n", xendev->key, response->id, status, strerror ( rc ) ); netdev_tx_complete_err ( netdev, iobuf, rc ); } } } /** * Poll for received packets * * @v netdev Network device */ static void netfront_poll_rx ( struct net_device *netdev ) { struct netfront_nic *netfront = netdev->priv; struct xen_device *xendev = netfront->xendev; struct netif_rx_response *response; struct io_buffer *iobuf; int status; int more; size_t len; int rc; /* Consume any unconsumed responses */ while ( RING_HAS_UNCONSUMED_RESPONSES ( &netfront->rx_fring ) ) { /* Get next response */ response = RING_GET_RESPONSE ( &netfront->rx_fring, netfront->rx_fring.rsp_cons++ ); /* Retrieve from descriptor ring */ iobuf = netfront_pull ( netfront, &netfront->rx, response->id ); status = response->status; more = ( response->flags & NETRXF_more_data ); /* Report errors */ if ( status < 0 ) { rc = -EIO_NETIF_RSP ( status ); DBGC2 ( netfront, "NETFRONT %s RX id %d error %d: %s\n", xendev->key, response->id, status, strerror ( rc ) ); netfront_discard ( netfront ); netdev_rx_err ( netdev, iobuf, rc ); continue; } /* Add to partial receive list */ len = status; iob_reserve ( iobuf, response->offset ); iob_put ( iobuf, len ); DBGC2 ( netfront, "NETFRONT %s RX id %d complete " "%#08lx+%zx%s\n", xendev->key, response->id, virt_to_phys ( iobuf->data ), len, ( more ? "..." : "" ) ); list_add_tail ( &iobuf->list, &netfront->rx_partial ); /* Wait until complete packet has been received */ if ( more ) continue; /* Reassemble complete packet */ iobuf = iob_concatenate ( &netfront->rx_partial ); if ( ! iobuf ) { DBGC2 ( netfront, "NETFRONT %s RX reassembly failed\n", xendev->key ); netfront_discard ( netfront ); netdev_rx_err ( netdev, NULL, -ENOMEM ); continue; } /* Hand off to network stack */ netdev_rx ( netdev, iobuf ); } } /** * Poll for completed and received packets * * @v netdev Network device */ static void netfront_poll ( struct net_device *netdev ) { /* Poll for TX completions */ netfront_poll_tx ( netdev ); /* Poll for RX completions */ netfront_poll_rx ( netdev ); /* Refill RX descriptor ring */ netfront_refill_rx ( netdev ); } /** Network device operations */ static struct net_device_operations netfront_operations = { .open = netfront_open, .close = netfront_close, .transmit = netfront_transmit, .poll = netfront_poll, }; /****************************************************************************** * * Xen device bus interface * ****************************************************************************** */ /** * Probe Xen device * * @v xendev Xen device * @ret rc Return status code */ static int netfront_probe ( struct xen_device *xendev ) { struct xen_hypervisor *xen = xendev->xen; struct net_device *netdev; struct netfront_nic *netfront; int rc; /* Allocate and initialise structure */ netdev = alloc_etherdev ( sizeof ( *netfront ) ); if ( ! netdev ) { rc = -ENOMEM; goto err_alloc; } netdev_init ( netdev, &netfront_operations ); netdev->dev = &xendev->dev; netfront = netdev->priv; netfront->xendev = xendev; netfront->netdev = netdev; INIT_LIST_HEAD ( &netfront->rx_partial ); DBGC ( netfront, "NETFRONT %s backend=\"%s\" in domain %ld\n", xendev->key, xendev->backend, xendev->backend_id ); /* Allocate grant references and initialise descriptor rings */ if ( ( rc = xengrant_alloc ( xen, netfront->refs, NETFRONT_REF_COUNT ) ) != 0 ) { DBGC ( netfront, "NETFRONT %s could not allocate grant " "references: %s\n", xendev->key, strerror ( rc ) ); goto err_grant_alloc; } netfront_init_ring ( &netfront->tx, "tx-ring-ref", netfront->refs[NETFRONT_REF_TX_RING], NETFRONT_NUM_TX_DESC, netfront->tx_iobufs, &netfront->refs[NETFRONT_REF_TX_BASE], netfront->tx_ids ); netfront_init_ring ( &netfront->rx, "rx-ring-ref", netfront->refs[NETFRONT_REF_RX_RING], NETFRONT_NUM_RX_DESC, netfront->rx_iobufs, &netfront->refs[NETFRONT_REF_RX_BASE], netfront->rx_ids ); /* Fetch MAC address */ if ( ( rc = netfront_read_mac ( netfront, netdev->hw_addr ) ) != 0 ) goto err_read_mac; /* Reset device. Ignore failures; allow the device to be * registered so that reset errors can be observed by the user * when attempting to open the device. */ netfront_reset ( netfront ); /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) goto err_register_netdev; /* Set initial link state */ netdev_link_down ( netdev ); /* Add to list of netfront devices */ list_add_tail ( &netfront->list, &netfront_devices ); xen_set_drvdata ( xendev, netdev ); return 0; list_del ( &netfront->list ); unregister_netdev ( netdev ); err_register_netdev: err_read_mac: xengrant_free ( xen, netfront->refs, NETFRONT_REF_COUNT ); err_grant_alloc: netdev_nullify ( netdev ); netdev_put ( netdev ); err_alloc: return rc; } /** * Remove Xen device * * @v xendev Xen device */ static void netfront_remove ( struct xen_device *xendev ) { struct net_device *netdev = xen_get_drvdata ( xendev ); struct netfront_nic *netfront = netdev->priv; struct xen_hypervisor *xen = xendev->xen; /* Remove from list of netfront devices */ list_del ( &netfront->list ); /* Unregister network device */ unregister_netdev ( netdev ); /* Free resources */ xengrant_free ( xen, netfront->refs, NETFRONT_REF_COUNT ); /* Free network device */ netdev_nullify ( netdev ); netdev_put ( netdev ); } /** Xen netfront driver */ struct xen_driver netfront_driver __xen_driver = { .name = "netfront", .type = "vif", .probe = netfront_probe, .remove = netfront_remove, }; /****************************************************************************** * * Emulated PCI device inhibitor * ****************************************************************************** */ /** * Inhibit emulated PCI devices * * @v netdev Network device * @v priv Private data * @ret rc Return status code */ static int netfront_net_probe ( struct net_device *netdev, void *priv __unused ) { struct netfront_nic *netfront; /* Inhibit emulated PCI devices matching an existing netfront device */ list_for_each_entry ( netfront, &netfront_devices, list ) { if ( ( netdev->dev != netfront->netdev->dev ) && ( netdev->ll_protocol->ll_addr_len == ETH_ALEN ) && ( memcmp ( netdev->hw_addr, netfront->netdev->hw_addr, ETH_ALEN ) == 0 ) ) { DBGC ( netfront, "NETFRONT %s inhibiting emulated %s " "%s\n", netfront->xendev->key, netdev->dev->driver_name, netdev->dev->name ); return -EEXIST; } } return 0; } /** Emulated PCI device inhibitor driver */ struct net_driver netfront_net_driver __net_driver = { .name = "netfront", .probe = netfront_net_probe, };