static void xenvif_tx_build_gops(struct xenvif *vif, int budget, unsigned *copy_ops, unsigned *map_ops) { struct gnttab_map_grant_ref *gop = vif->tx_map_ops, *request_gop; struct sk_buff *skb; int ret; while (skb_queue_len(&vif->tx_queue) < budget) { struct xen_netif_tx_request txreq; struct xen_netif_tx_request txfrags[XEN_NETBK_LEGACY_SLOTS_MAX]; struct xen_netif_extra_info extras[XEN_NETIF_EXTRA_TYPE_MAX-1]; u16 pending_idx; RING_IDX idx; int work_to_do; unsigned int data_len; pending_ring_idx_t index; if (vif->tx.sring->req_prod - vif->tx.req_cons > XEN_NETIF_TX_RING_SIZE) { netdev_err(vif->dev, "Impossible number of requests. " "req_prod %d, req_cons %d, size %ld\n", vif->tx.sring->req_prod, vif->tx.req_cons, XEN_NETIF_TX_RING_SIZE); xenvif_fatal_tx_err(vif); break; } work_to_do = RING_HAS_UNCONSUMED_REQUESTS(&vif->tx); if (!work_to_do) break; idx = vif->tx.req_cons; rmb(); /* Ensure that we see the request before we copy it. */ memcpy(&txreq, RING_GET_REQUEST(&vif->tx, idx), sizeof(txreq)); /* Credit-based scheduling. */ if (txreq.size > vif->remaining_credit && tx_credit_exceeded(vif, txreq.size)) break; vif->remaining_credit -= txreq.size; work_to_do--; vif->tx.req_cons = ++idx; memset(extras, 0, sizeof(extras)); if (txreq.flags & XEN_NETTXF_extra_info) { work_to_do = xenvif_get_extras(vif, extras, work_to_do); idx = vif->tx.req_cons; if (unlikely(work_to_do < 0)) break; } ret = xenvif_count_requests(vif, &txreq, txfrags, work_to_do); if (unlikely(ret < 0)) break; idx += ret; if (unlikely(txreq.size < ETH_HLEN)) { netdev_dbg(vif->dev, "Bad packet size: %d\n", txreq.size); xenvif_tx_err(vif, &txreq, idx); break; } /* No crossing a page as the payload mustn't fragment. */ if (unlikely((txreq.offset + txreq.size) > PAGE_SIZE)) { netdev_err(vif->dev, "txreq.offset: %x, size: %u, end: %lu\n", txreq.offset, txreq.size, (txreq.offset&~PAGE_MASK) + txreq.size); xenvif_fatal_tx_err(vif); break; } index = pending_index(vif->pending_cons); pending_idx = vif->pending_ring[index]; data_len = (txreq.size > PKT_PROT_LEN && ret < XEN_NETBK_LEGACY_SLOTS_MAX) ? PKT_PROT_LEN : txreq.size; skb = xenvif_alloc_skb(data_len); if (unlikely(skb == NULL)) { netdev_dbg(vif->dev, "Can't allocate a skb in start_xmit.\n"); xenvif_tx_err(vif, &txreq, idx); break; } if (extras[XEN_NETIF_EXTRA_TYPE_GSO - 1].type) { struct xen_netif_extra_info *gso; gso = &extras[XEN_NETIF_EXTRA_TYPE_GSO - 1]; if (xenvif_set_skb_gso(vif, skb, gso)) { /* Failure in xenvif_set_skb_gso is fatal. */ kfree_skb(skb); break; } } XENVIF_TX_CB(skb)->pending_idx = pending_idx; __skb_put(skb, data_len); vif->tx_copy_ops[*copy_ops].source.u.ref = txreq.gref; vif->tx_copy_ops[*copy_ops].source.domid = vif->domid; vif->tx_copy_ops[*copy_ops].source.offset = txreq.offset; vif->tx_copy_ops[*copy_ops].dest.u.gmfn = virt_to_mfn(skb->data); vif->tx_copy_ops[*copy_ops].dest.domid = DOMID_SELF; vif->tx_copy_ops[*copy_ops].dest.offset = offset_in_page(skb->data); vif->tx_copy_ops[*copy_ops].len = data_len; vif->tx_copy_ops[*copy_ops].flags = GNTCOPY_source_gref; (*copy_ops)++; skb_shinfo(skb)->nr_frags = ret; if (data_len < txreq.size) { skb_shinfo(skb)->nr_frags++; frag_set_pending_idx(&skb_shinfo(skb)->frags[0], pending_idx); xenvif_tx_create_map_op(vif, pending_idx, &txreq, gop); gop++; } else { frag_set_pending_idx(&skb_shinfo(skb)->frags[0], INVALID_PENDING_IDX); memcpy(&vif->pending_tx_info[pending_idx].req, &txreq, sizeof(txreq)); } vif->pending_cons++; request_gop = xenvif_get_requests(vif, skb, txfrags, gop); if (request_gop == NULL) { kfree_skb(skb); xenvif_tx_err(vif, &txreq, idx); break; } gop = request_gop; __skb_queue_tail(&vif->tx_queue, skb); vif->tx.req_cons = idx; if (((gop-vif->tx_map_ops) >= ARRAY_SIZE(vif->tx_map_ops)) || (*copy_ops >= ARRAY_SIZE(vif->tx_copy_ops))) break; } (*map_ops) = gop - vif->tx_map_ops; return; }
static unsigned xenvif_tx_build_gops(struct xenvif *vif, int budget) { struct gnttab_copy *gop = vif->tx_copy_ops, *request_gop; struct sk_buff *skb; int ret; while ((nr_pending_reqs(vif) + XEN_NETBK_LEGACY_SLOTS_MAX < MAX_PENDING_REQS) && (skb_queue_len(&vif->tx_queue) < budget)) { struct xen_netif_tx_request txreq; struct xen_netif_tx_request txfrags[XEN_NETBK_LEGACY_SLOTS_MAX]; struct page *page; struct xen_netif_extra_info extras[XEN_NETIF_EXTRA_TYPE_MAX-1]; u16 pending_idx; RING_IDX idx; int work_to_do; unsigned int data_len; pending_ring_idx_t index; if (vif->tx.sring->req_prod - vif->tx.req_cons > XEN_NETIF_TX_RING_SIZE) { netdev_err(vif->dev, "Impossible number of requests. " "req_prod %d, req_cons %d, size %ld\n", vif->tx.sring->req_prod, vif->tx.req_cons, XEN_NETIF_TX_RING_SIZE); xenvif_fatal_tx_err(vif); continue; } work_to_do = RING_HAS_UNCONSUMED_REQUESTS(&vif->tx); if (!work_to_do) break; idx = vif->tx.req_cons; rmb(); /* Ensure that we see the request before we copy it. */ memcpy(&txreq, RING_GET_REQUEST(&vif->tx, idx), sizeof(txreq)); /* Credit-based scheduling. */ if (txreq.size > vif->remaining_credit && tx_credit_exceeded(vif, txreq.size)) break; vif->remaining_credit -= txreq.size; work_to_do--; vif->tx.req_cons = ++idx; memset(extras, 0, sizeof(extras)); if (txreq.flags & XEN_NETTXF_extra_info) { work_to_do = xenvif_get_extras(vif, extras, work_to_do); idx = vif->tx.req_cons; if (unlikely(work_to_do < 0)) break; } ret = xenvif_count_requests(vif, &txreq, txfrags, work_to_do); if (unlikely(ret < 0)) break; idx += ret; if (unlikely(txreq.size < ETH_HLEN)) { netdev_dbg(vif->dev, "Bad packet size: %d\n", txreq.size); xenvif_tx_err(vif, &txreq, idx); break; } /* No crossing a page as the payload mustn't fragment. */ if (unlikely((txreq.offset + txreq.size) > PAGE_SIZE)) { netdev_err(vif->dev, "txreq.offset: %x, size: %u, end: %lu\n", txreq.offset, txreq.size, (txreq.offset&~PAGE_MASK) + txreq.size); xenvif_fatal_tx_err(vif); break; } index = pending_index(vif->pending_cons); pending_idx = vif->pending_ring[index]; data_len = (txreq.size > PKT_PROT_LEN && ret < XEN_NETBK_LEGACY_SLOTS_MAX) ? PKT_PROT_LEN : txreq.size; skb = alloc_skb(data_len + NET_SKB_PAD + NET_IP_ALIGN, GFP_ATOMIC | __GFP_NOWARN); if (unlikely(skb == NULL)) { netdev_dbg(vif->dev, "Can't allocate a skb in start_xmit.\n"); xenvif_tx_err(vif, &txreq, idx); break; } /* Packets passed to netif_rx() must have some headroom. */ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN); if (extras[XEN_NETIF_EXTRA_TYPE_GSO - 1].type) { struct xen_netif_extra_info *gso; gso = &extras[XEN_NETIF_EXTRA_TYPE_GSO - 1]; if (xenvif_set_skb_gso(vif, skb, gso)) { /* Failure in xenvif_set_skb_gso is fatal. */ kfree_skb(skb); break; } } /* XXX could copy straight to head */ page = xenvif_alloc_page(vif, pending_idx); if (!page) { kfree_skb(skb); xenvif_tx_err(vif, &txreq, idx); break; } gop->source.u.ref = txreq.gref; gop->source.domid = vif->domid; gop->source.offset = txreq.offset; gop->dest.u.gmfn = virt_to_mfn(page_address(page)); gop->dest.domid = DOMID_SELF; gop->dest.offset = txreq.offset; gop->len = txreq.size; gop->flags = GNTCOPY_source_gref; gop++; memcpy(&vif->pending_tx_info[pending_idx].req, &txreq, sizeof(txreq)); vif->pending_tx_info[pending_idx].head = index; *((u16 *)skb->data) = pending_idx; __skb_put(skb, data_len); skb_shinfo(skb)->nr_frags = ret; if (data_len < txreq.size) { skb_shinfo(skb)->nr_frags++; frag_set_pending_idx(&skb_shinfo(skb)->frags[0], pending_idx); } else { frag_set_pending_idx(&skb_shinfo(skb)->frags[0], INVALID_PENDING_IDX); } vif->pending_cons++; request_gop = xenvif_get_requests(vif, skb, txfrags, gop); if (request_gop == NULL) { kfree_skb(skb); xenvif_tx_err(vif, &txreq, idx); break; } gop = request_gop; __skb_queue_tail(&vif->tx_queue, skb); vif->tx.req_cons = idx; if ((gop-vif->tx_copy_ops) >= ARRAY_SIZE(vif->tx_copy_ops)) break; } return gop - vif->tx_copy_ops; }