* @v xid Transaction ID
* @v data Data
* @v len Length of data
- * @v iobuf I/O buffer, or NULL if allocation failed
+ * @v list List of I/O buffers
* @ret rc Return status code
*/
static int netvsc_recv_data ( struct vmbus_device *vmdev, uint64_t xid,
const void *data, size_t len,
- struct io_buffer *iobuf ) {
+ struct list_head *list ) {
struct rndis_device *rndis = vmbus_get_drvdata ( vmdev );
struct netvsc_device *netvsc = rndis->priv;
const struct netvsc_rndis_message *msg = data;
+ struct io_buffer *iobuf;
+ struct io_buffer *tmp;
int rc;
/* Sanity check */
goto err_sanity;
}
- /* Send completion back to host (even if I/O buffer was missing) */
+ /* Send completion back to host */
if ( ( rc = vmbus_send_completion ( vmdev, xid, NULL, 0 ) ) != 0 ) {
DBGC ( netvsc, "NETVSC %s could not send completion: %s\n",
netvsc->name, strerror ( rc ) );
goto err_completion;
}
- /* Hand off to RNDIS (even if I/O buffer was missing) */
- rndis_rx ( rndis, iob_disown ( iobuf ) );
+ /* Hand off to RNDIS */
+ list_for_each_entry_safe ( iobuf, tmp, list, list ) {
+ list_del ( &iobuf->list );
+ rndis_rx ( rndis, iob_disown ( iobuf ) );
+ }
return 0;
err_completion:
err_sanity:
- free_iob ( iobuf );
+ list_for_each_entry_safe ( iobuf, tmp, list, list ) {
+ list_del ( &iobuf->list );
+ free_iob ( iobuf );
+ }
return rc;
}
* @v xid Transaction ID
* @v data Data
* @v len Length of data
- * @v iobuf I/O buffer, or NULL if allocation failed
+ * @v list List of I/O buffers
* @ret rc Return status code
*
* This function takes ownership of the I/O buffer. It should
* eventually call vmbus_send_completion() to indicate to the
* host that the buffer can be reused.
- *
- * Note that this function will be called even if we failed to
- * allocate or populate the I/O buffer; this is to allow for a
- * completion to be sent even in the event of a transient
- * memory shortage failure.
*/
int ( * recv_data ) ( struct vmbus_device *vmdev, uint64_t xid,
const void *data, size_t len,
- struct io_buffer *iobuf );
+ struct list_head *list );
/**
* Handle received completion packet
*
}
/**
- * Construct I/O buffer from transfer pages
+ * Construct I/O buffer list from transfer pages
*
* @v vmdev VMBus device
* @v header Transfer page header
- * @ret iobuf I/O buffer, or NULL on error
+ * @v list I/O buffer list to populate
+ * @ret rc Return status code
*/
-static struct io_buffer *
-vmbus_xfer_page_iobuf ( struct vmbus_device *vmdev,
- struct vmbus_packet_header *header ) {
+static int vmbus_xfer_page_iobufs ( struct vmbus_device *vmdev,
+ struct vmbus_packet_header *header,
+ struct list_head *list ) {
struct vmbus_xfer_page_header *page_header =
container_of ( header, struct vmbus_xfer_page_header, header );
struct vmbus_xfer_pages *pages;
struct io_buffer *iobuf;
- size_t total_len;
+ struct io_buffer *tmp;
size_t len;
size_t offset;
unsigned int range_count;
/* Locate page set */
pages = vmbus_xfer_pages ( vmdev, page_header->pageset );
- if ( ! pages )
+ if ( ! pages ) {
+ rc = -ENOENT;
goto err_pages;
-
- /* Determine total length */
- range_count = le32_to_cpu ( page_header->range_count );
- for ( total_len = 0, i = 0 ; i < range_count ; i++ ) {
- len = le32_to_cpu ( page_header->range[i].len );
- total_len += len;
}
- /* Allocate I/O buffer */
- iobuf = alloc_iob ( total_len );
- if ( ! iobuf ) {
- DBGC ( vmdev, "VMBUS %s could not allocate %zd-byte I/O "
- "buffer\n", vmdev->dev.name, total_len );
- goto err_alloc;
- }
-
- /* Populate I/O buffer */
+ /* Allocate and populate I/O buffers */
+ range_count = le32_to_cpu ( page_header->range_count );
for ( i = 0 ; i < range_count ; i++ ) {
+
+ /* Parse header */
len = le32_to_cpu ( page_header->range[i].len );
offset = le32_to_cpu ( page_header->range[i].offset );
+
+ /* Allocate I/O buffer */
+ iobuf = alloc_iob ( len );
+ if ( ! iobuf ) {
+ DBGC ( vmdev, "VMBUS %s could not allocate %zd-byte "
+ "I/O buffer\n", vmdev->dev.name, len );
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+
+ /* Add I/O buffer to list */
+ list_add ( &iobuf->list, list );
+
+ /* Populate I/O buffer */
if ( ( rc = pages->op->copy ( pages, iob_put ( iobuf, len ),
offset, len ) ) != 0 ) {
DBGC ( vmdev, "VMBUS %s could not populate I/O buffer "
}
}
- return iobuf;
+ return 0;
err_copy:
- free_iob ( iobuf );
err_alloc:
+ list_for_each_entry_safe ( iobuf, tmp, list, list ) {
+ list_del ( &iobuf->list );
+ free_iob ( iobuf );
+ }
err_pages:
- return NULL;
+ return rc;
}
/**
*/
int vmbus_poll ( struct vmbus_device *vmdev ) {
struct vmbus_packet_header *header = vmdev->packet;
- struct io_buffer *iobuf;
+ struct list_head list;
void *data;
size_t header_len;
size_t len;
DBGC2_HDA ( vmdev, old_cons, header, ring_len );
assert ( ( ( cons - old_cons ) & ( vmdev->in_len - 1 ) ) == ring_len );
+ /* Allocate I/O buffers, if applicable */
+ INIT_LIST_HEAD ( &list );
+ if ( header->type == cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) ) {
+ if ( ( rc = vmbus_xfer_page_iobufs ( vmdev, header,
+ &list ) ) != 0 )
+ return rc;
+ }
+
/* Update producer index */
rmb();
vmdev->in->cons = cpu_to_le32 ( cons );
break;
case cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) :
- iobuf = vmbus_xfer_page_iobuf ( vmdev, header );
- /* Call recv_data() even if I/O buffer allocation
- * failed, to allow for completions to be sent.
- */
if ( ( rc = vmdev->op->recv_data ( vmdev, xid, data, len,
- iob_disown ( iobuf ) ) )!=0){
+ &list ) ) != 0 ) {
DBGC ( vmdev, "VMBUS %s could not handle data packet: "
"%s\n", vmdev->dev.name, strerror ( rc ) );
return rc;
* Receive packet from underlying transport layer
*
* @v rndis RNDIS device
- * @v iobuf I/O buffer, or NULL if allocation failed
+ * @v iobuf I/O buffer
*/
void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf ) {
struct net_device *netdev = rndis->netdev;
struct rndis_header *header;
- struct io_buffer *msg;
- size_t len;
unsigned int type;
int rc;
- /* Record dropped packet if I/O buffer is missing */
- if ( ! iobuf ) {
- DBGC2 ( rndis, "RNDIS %s received dropped packet\n",
- rndis->name );
- rc = -ENOBUFS;
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( *header ) ) {
+ DBGC ( rndis, "RNDIS %s received underlength packet:\n",
+ rndis->name );
+ DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
+ rc = -EINVAL;
goto drop;
}
+ header = iobuf->data;
- /* Split packet into messages */
- while ( iobuf ) {
-
- /* Sanity check */
- if ( iob_len ( iobuf ) < sizeof ( *header ) ) {
- DBGC ( rndis, "RNDIS %s received underlength packet:\n",
- rndis->name );
- DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
- rc = -EINVAL;
- goto drop;
- }
- header = iobuf->data;
-
- /* Parse and check header */
- type = le32_to_cpu ( header->type );
- len = le32_to_cpu ( header->len );
- if ( ( len < sizeof ( *header ) ) ||
- ( len > iob_len ( iobuf ) ) ) {
- DBGC ( rndis, "RNDIS %s received malformed packet:\n",
- rndis->name );
- DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
- rc = -EINVAL;
- goto drop;
- }
-
- /* Split buffer if required */
- if ( len < iob_len ( iobuf ) ) {
- msg = iob_split ( iobuf, len );
- if ( ! msg ) {
- rc = -ENOMEM;
- goto drop;
- }
- } else {
- msg = iobuf;
- iobuf = NULL;
- }
-
- /* Strip header */
- iob_pull ( msg, sizeof ( *header ) );
+ /* Parse and strip header */
+ type = le32_to_cpu ( header->type );
+ iob_pull ( iobuf, sizeof ( *header ) );
- /* Handle message */
- rndis_rx_message ( rndis, iob_disown ( msg ), type );
- }
+ /* Handle message */
+ rndis_rx_message ( rndis, iob_disown ( iobuf ), type );
return;