diff options
Diffstat (limited to 'src/drivers/net')
| -rw-r--r-- | src/drivers/net/netfront.c | 90 |
1 files changed, 62 insertions, 28 deletions
diff --git a/src/drivers/net/netfront.c b/src/drivers/net/netfront.c index 18913c08d..ef04f5c5c 100644 --- a/src/drivers/net/netfront.c +++ b/src/drivers/net/netfront.c @@ -292,8 +292,13 @@ static int netfront_create_ring ( struct netfront_nic *netfront, } /* Grant access to shared ring */ - xengrant_permit_access ( xen, ring->ref, xendev->backend_id, 0, - ring->sring.raw ); + if ( ( rc = xengrant_permit_access ( xen, ring->ref, xendev->backend_id, + 0, ring->sring.raw ) ) != 0 ) { + DBGC ( netfront, "NETFRONT %s could not permit access to " + "%#08lx: %s\n", xendev->key, + virt_to_phys ( ring->sring.raw ), strerror ( rc ) ); + goto err_permit_access; + } /* Publish shared ring reference */ if ( ( rc = netfront_write_num ( netfront, ring->ref_key, @@ -309,6 +314,7 @@ static int netfront_create_ring ( struct netfront_nic *netfront, netfront_rm ( netfront, ring->ref_key ); err_write_num: xengrant_invalidate ( xen, ring->ref ); + err_permit_access: free_dma ( ring->sring.raw, PAGE_SIZE ); err_alloc: return rc; @@ -320,39 +326,53 @@ static int netfront_create_ring ( struct netfront_nic *netfront, * @v netfront Netfront device * @v ring Descriptor ring * @v iobuf I/O buffer + * @v id Buffer ID to fill in * @v ref Grant reference to fill in - * @ret id Buffer ID + * @ret rc Return status code * * The caller is responsible for ensuring that there is space in the * ring. */ -static unsigned int netfront_push ( struct netfront_nic *netfront, - struct netfront_ring *ring, - struct io_buffer *iobuf, - grant_ref_t *ref ) { +static int netfront_push ( struct netfront_nic *netfront, + struct netfront_ring *ring, 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 id; + unsigned int next_id; + unsigned int next_ref; + int rc; /* Sanity check */ assert ( ! netfront_ring_is_full ( ring ) ); /* Allocate buffer ID */ - id = ring->ids[ ( ring->id_prod++ ) & ( ring->count - 1 ) ]; - - /* Store I/O buffer */ - assert ( ring->iobufs[id] == NULL ); - ring->iobufs[id] = iobuf; + next_id = ring->ids[ ring->id_prod & ( ring->count - 1 ) ]; + next_ref = ring->refs[next_id]; /* Grant access to I/O buffer page. I/O buffers are naturally * aligned, so we never need to worry about crossing a page * boundary. */ - *ref = ring->refs[id]; - xengrant_permit_access ( xen, ring->refs[id], xendev->backend_id, 0, - iobuf->data ); + if ( ( rc = xengrant_permit_access ( xen, next_ref, xendev->backend_id, + 0, iobuf->data ) ) != 0 ) { + DBGC ( netfront, "NETFRONT %s could not permit access to " + "%#08lx: %s\n", xendev->key, + virt_to_phys ( iobuf->data ), strerror ( rc ) ); + return rc; + } + + /* Store I/O buffer */ + assert ( ring->iobufs[next_id] == NULL ); + ring->iobufs[next_id] = iobuf; - return id; + /* Consume buffer ID */ + ring->id_prod++; + + /* Return buffer ID and grant reference */ + *id = next_id; + *ref = next_ref; + + return 0; } /** @@ -431,13 +451,15 @@ static void netfront_destroy_ring ( struct netfront_nic *netfront, /** * Refill receive descriptor ring * - * @v netfront Netfront device + * @v netdev Network device */ -static void netfront_refill_rx ( struct netfront_nic *netfront ) { +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; int notify; + int rc; /* Do nothing if ring is already full */ if ( netfront_ring_is_full ( &netfront->rx ) ) @@ -455,13 +477,20 @@ static void netfront_refill_rx ( struct netfront_nic *netfront ) { /* Add to descriptor ring */ request = RING_GET_REQUEST ( &netfront->rx_fring, - netfront->rx_fring.req_prod_pvt++); - request->id = netfront_push ( netfront, &netfront->rx, iobuf, - &request->gref ); + netfront->rx_fring.req_prod_pvt ); + if ( ( rc = netfront_push ( netfront, &netfront->rx, + 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, virt_to_phys ( iobuf->data ), iob_tailroom ( iobuf ) ); + /* Move to next descriptor */ + netfront->rx_fring.req_prod_pvt++; + } while ( ! netfront_ring_is_full ( &netfront->rx ) ); /* Push new descriptors and notify backend if applicable */ @@ -526,7 +555,7 @@ static int netfront_open ( struct net_device *netdev ) { } /* Refill receive descriptor ring */ - netfront_refill_rx ( netfront ); + netfront_refill_rx ( netdev ); /* Set link up */ netdev_link_up ( netdev ); @@ -614,6 +643,7 @@ static int netfront_transmit ( struct net_device *netdev, struct xen_device *xendev = netfront->xendev; struct netif_tx_request *request; int notify; + int rc; /* Check that we have space in the ring */ if ( netfront_ring_is_full ( &netfront->tx ) ) { @@ -624,9 +654,11 @@ static int netfront_transmit ( struct net_device *netdev, /* Add to descriptor ring */ request = RING_GET_REQUEST ( &netfront->tx_fring, - netfront->tx_fring.req_prod_pvt++ ); - request->id = netfront_push ( netfront, &netfront->tx, iobuf, - &request->gref ); + netfront->tx_fring.req_prod_pvt ); + if ( ( rc = netfront_push ( netfront, &netfront->tx, iobuf, + &request->id, &request->gref ) ) != 0 ) { + return rc; + } request->offset = ( virt_to_phys ( iobuf->data ) & ( PAGE_SIZE - 1 ) ); request->flags = NETTXF_data_validated; request->size = iob_len ( iobuf ); @@ -634,6 +666,9 @@ static int netfront_transmit ( struct net_device *netdev, xendev->key, request->id, request->gref, virt_to_phys ( iobuf->data ), iob_len ( iobuf ) ); + /* Consume descriptor */ + netfront->tx_fring.req_prod_pvt++; + /* Push new descriptor and notify backend if applicable */ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->tx_fring, notify ); if ( notify ) @@ -727,7 +762,6 @@ static void netfront_poll_rx ( struct net_device *netdev ) { * @v netdev Network device */ static void netfront_poll ( struct net_device *netdev ) { - struct netfront_nic *netfront = netdev->priv; /* Poll for TX completions */ netfront_poll_tx ( netdev ); @@ -736,7 +770,7 @@ static void netfront_poll ( struct net_device *netdev ) { netfront_poll_rx ( netdev ); /* Refill RX descriptor ring */ - netfront_refill_rx ( netfront ); + netfront_refill_rx ( netdev ); } /** Network device operations */ |
