From patchwork Fri Dec 11 12:49:53 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shirley Ma X-Patchwork-Id: 66647 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id nBBCoW9p026633 for ; Fri, 11 Dec 2009 12:50:32 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756650AbZLKMtv (ORCPT ); Fri, 11 Dec 2009 07:49:51 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1756208AbZLKMtv (ORCPT ); Fri, 11 Dec 2009 07:49:51 -0500 Received: from e3.ny.us.ibm.com ([32.97.182.143]:35532 "EHLO e3.ny.us.ibm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756091AbZLKMtu (ORCPT ); Fri, 11 Dec 2009 07:49:50 -0500 Received: from d01relay07.pok.ibm.com (d01relay07.pok.ibm.com [9.56.227.147]) by e3.ny.us.ibm.com (8.14.3/8.13.1) with ESMTP id nBBCeofP007785; Fri, 11 Dec 2009 07:40:50 -0500 Received: from d01av01.pok.ibm.com (d01av01.pok.ibm.com [9.56.224.215]) by d01relay07.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id nBBCnu4N1716346; Fri, 11 Dec 2009 07:49:56 -0500 Received: from d01av01.pok.ibm.com (loopback [127.0.0.1]) by d01av01.pok.ibm.com (8.14.3/8.13.1/NCO v10.0 AVout) with ESMTP id nBBCntpv013649; Fri, 11 Dec 2009 07:49:55 -0500 Received: from [9.65.57.224] (sig-9-65-57-224.mts.ibm.com [9.65.57.224]) by d01av01.pok.ibm.com (8.14.3/8.13.1/NCO v10.0 AVin) with ESMTP id nBBCnr1N013620; Fri, 11 Dec 2009 07:49:54 -0500 Subject: [PATCH v2 4/4] Defer skb allocation -- change allocation & receiving in recv path From: Shirley Ma To: Rusty Russell Cc: "Michael S. Tsirkin" , Avi Kivity , netdev@vger.kernel.org, kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Anthony Liguori In-Reply-To: <1260534506.30371.6.camel@localhost.localdomain> References: <1258697359.7416.14.camel@localhost.localdomain> <200911231123.18898.rusty@rustcorp.com.au> <20091208122134.GA17286@redhat.com> <1260534506.30371.6.camel@localhost.localdomain> Date: Fri, 11 Dec 2009 04:49:53 -0800 Message-Id: <1260535793.30371.27.camel@localhost.localdomain> Mime-Version: 1.0 X-Mailer: Evolution 2.24.5 (2.24.5-2.fc10) Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index dde8060..b919169 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -270,99 +270,44 @@ static struct sk_buff *receive_mergeable(struct virtnet_info *vi, return skb; } -static void receive_skb(struct net_device *dev, struct sk_buff *skb, - unsigned len) +static void receive_buf(struct net_device *dev, void *buf, unsigned int len) { struct virtnet_info *vi = netdev_priv(dev); - struct skb_vnet_hdr *hdr = skb_vnet_hdr(skb); - int err; - int i; + struct sk_buff *skb = (struct sk_buff *)buf; + struct page *page = (struct page *)buf; + struct skb_vnet_hdr *hdr; if (unlikely(len < sizeof(struct virtio_net_hdr) + ETH_HLEN)) { pr_debug("%s: short packet %i\n", dev->name, len); dev->stats.rx_length_errors++; - goto drop; - } - - if (vi->mergeable_rx_bufs) { - unsigned int copy; - char *p = page_address(skb_shinfo(skb)->frags[0].page); - - if (len > PAGE_SIZE) - len = PAGE_SIZE; - len -= sizeof(struct virtio_net_hdr_mrg_rxbuf); - - memcpy(&hdr->mhdr, p, sizeof(hdr->mhdr)); - p += sizeof(hdr->mhdr); - - copy = len; - if (copy > skb_tailroom(skb)) - copy = skb_tailroom(skb); - - memcpy(skb_put(skb, copy), p, copy); - - len -= copy; - - if (!len) { - give_pages(vi, skb_shinfo(skb)->frags[0].page); - skb_shinfo(skb)->nr_frags--; + if (vi->mergeable_rx_bufs || vi->big_packets) { + give_pages(vi, page); + return; } else { - skb_shinfo(skb)->frags[0].page_offset += - sizeof(hdr->mhdr) + copy; - skb_shinfo(skb)->frags[0].size = len; - skb->data_len += len; - skb->len += len; - } - - while (--hdr->mhdr.num_buffers) { - struct sk_buff *nskb; - - i = skb_shinfo(skb)->nr_frags; - if (i >= MAX_SKB_FRAGS) { - pr_debug("%s: packet too long %d\n", dev->name, - len); - dev->stats.rx_length_errors++; - goto drop; - } - - nskb = vi->rvq->vq_ops->get_buf(vi->rvq, &len); - if (!nskb) { - pr_debug("%s: rx error: %d buffers missing\n", - dev->name, hdr->mhdr.num_buffers); - dev->stats.rx_length_errors++; - goto drop; - } - - __skb_unlink(nskb, &vi->recv); - vi->num--; - - skb_shinfo(skb)->frags[i] = skb_shinfo(nskb)->frags[0]; - skb_shinfo(nskb)->nr_frags = 0; - kfree_skb(nskb); - - if (len > PAGE_SIZE) - len = PAGE_SIZE; - - skb_shinfo(skb)->frags[i].size = len; - skb_shinfo(skb)->nr_frags++; - skb->data_len += len; - skb->len += len; + skb_unlink(skb, &vi->recv); + goto drop; } - } else { - len -= sizeof(hdr->hdr); + } - if (len <= MAX_PACKET_LEN) - trim_pages(vi, skb); + if (vi->mergeable_rx_bufs) + skb = receive_mergeable(vi, page, len); + else if (vi->big_packets) + skb = receive_big(vi, page, len); + else { + __skb_unlink(skb, &vi->recv); + len -= sizeof(struct virtio_net_hdr); + skb_trim(skb, len); + } - err = pskb_trim(skb, len); - if (err) { - pr_debug("%s: pskb_trim failed %i %d\n", dev->name, - len, err); - dev->stats.rx_dropped++; - goto drop; - } + if (unlikely(!skb)) { + dev->stats.rx_dropped++; + /* only for mergeable buf and big packets */ + give_pages(vi, page); + return; } + hdr = skb_vnet_hdr(skb); + skb->truesize += skb->data_len; dev->stats.rx_bytes += skb->len; dev->stats.rx_packets++; @@ -520,105 +465,22 @@ static int add_recvbuf_mergeable(struct virtnet_info *vi, gfp_t gfp, bool *oom) return err; } -static bool try_fill_recv_maxbufs(struct virtnet_info *vi, gfp_t gfp) -{ - struct sk_buff *skb; - struct scatterlist sg[2+MAX_SKB_FRAGS]; - int num, err, i; - bool oom = false; - - sg_init_table(sg, 2+MAX_SKB_FRAGS); - do { - struct skb_vnet_hdr *hdr; - - skb = netdev_alloc_skb_ip_align(vi->dev, MAX_PACKET_LEN); - if (unlikely(!skb)) { - oom = true; - break; - } - - skb_put(skb, MAX_PACKET_LEN); - - hdr = skb_vnet_hdr(skb); - sg_set_buf(sg, &hdr->hdr, sizeof(hdr->hdr)); - - if (vi->big_packets) { - for (i = 0; i < MAX_SKB_FRAGS; i++) { - skb_frag_t *f = &skb_shinfo(skb)->frags[i]; - f->page = get_a_page(vi, gfp); - if (!f->page) - break; - - f->page_offset = 0; - f->size = PAGE_SIZE; - - skb->data_len += PAGE_SIZE; - skb->len += PAGE_SIZE; - - skb_shinfo(skb)->nr_frags++; - } - } - - num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1; - skb_queue_head(&vi->recv, skb); - - err = vi->rvq->vq_ops->add_buf(vi->rvq, sg, 0, num, skb); - if (err < 0) { - skb_unlink(skb, &vi->recv); - trim_pages(vi, skb); - kfree_skb(skb); - break; - } - vi->num++; - } while (err >= num); - if (unlikely(vi->num > vi->max)) - vi->max = vi->num; - vi->rvq->vq_ops->kick(vi->rvq); - return !oom; -} - /* Returns false if we couldn't fill entirely (OOM). */ static bool try_fill_recv(struct virtnet_info *vi, gfp_t gfp) { - struct sk_buff *skb; - struct scatterlist sg[1]; int err; bool oom = false; - if (!vi->mergeable_rx_bufs) - return try_fill_recv_maxbufs(vi, gfp); - do { - skb_frag_t *f; - - skb = netdev_alloc_skb_ip_align(vi->dev, GOOD_COPY_LEN); - if (unlikely(!skb)) { - oom = true; - break; - } - - f = &skb_shinfo(skb)->frags[0]; - f->page = get_a_page(vi, gfp); - if (!f->page) { - oom = true; - kfree_skb(skb); - break; - } - - f->page_offset = 0; - f->size = PAGE_SIZE; - - skb_shinfo(skb)->nr_frags++; - - sg_init_one(sg, page_address(f->page), PAGE_SIZE); - skb_queue_head(&vi->recv, skb); + if (vi->mergeable_rx_bufs) + err = add_recvbuf_mergeable(vi, gfp, &oom); + else if (vi->big_packets) + err = add_recvbuf_big(vi, gfp, &oom); + else + err = add_recvbuf_small(vi, gfp, &oom); - err = vi->rvq->vq_ops->add_buf(vi->rvq, sg, 0, 1, skb); - if (err < 0) { - skb_unlink(skb, &vi->recv); - kfree_skb(skb); + if (oom || err < 0) break; - } vi->num++; } while (err > 0); if (unlikely(vi->num > vi->max)) @@ -657,14 +519,13 @@ static void refill_work(struct work_struct *work) static int virtnet_poll(struct napi_struct *napi, int budget) { struct virtnet_info *vi = container_of(napi, struct virtnet_info, napi); - struct sk_buff *skb = NULL; + void *buf = NULL; unsigned int len, received = 0; again: while (received < budget && - (skb = vi->rvq->vq_ops->get_buf(vi->rvq, &len)) != NULL) { - __skb_unlink(skb, &vi->recv); - receive_skb(vi->dev, skb, len); + (buf = vi->rvq->vq_ops->get_buf(vi->rvq, &len)) != NULL) { + receive_buf(vi->dev, buf, len); vi->num--; received++; } @@ -1205,6 +1066,7 @@ static void __devexit virtnet_remove(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; struct sk_buff *skb; + int freed; /* Stop all the virtqueues. */ vdev->config->reset(vdev); @@ -1216,11 +1078,14 @@ static void __devexit virtnet_remove(struct virtio_device *vdev) } __skb_queue_purge(&vi->send); - BUG_ON(vi->num != 0); - unregister_netdev(vi->dev); cancel_delayed_work_sync(&vi->refill); + freed = vi->rvq->vq_ops->destroy_bufs(vi->rvq, virtio_net_free_pages); + vi->num -= freed; + + BUG_ON(vi->num != 0); + vdev->config->del_vqs(vi->vdev); while (vi->pages)