---
 drivers/block/virtio_blk.c    |   31 ++++++++++++++++++-------------
 drivers/char/virtio_console.c |    4 ++--
 drivers/net/virtio_net.c      |   31 +++++++++++++++++++------------
 3 files changed, 39 insertions(+), 27 deletions(-)

diff -r 913cb29cde3c drivers/block/virtio_blk.c
--- a/drivers/block/virtio_blk.c	Mon Jan 07 13:55:26 2008 +1100
+++ b/drivers/block/virtio_blk.c	Mon Jan 07 14:26:10 2008 +1100
@@ -26,8 +26,10 @@ struct virtio_blk
 
 	mempool_t *pool;
 
-	/* Scatterlist: can be too big for stack. */
-	struct scatterlist sg[VIRTIO_MAX_SG];
+	/* Scatterlist: can be too big for stack. +1 required for chaining. */
+	struct scatterlist out_sg[2];
+	struct scatterlist in_sg[1];
+	struct scatterlist req_sg[MAX_PHYS_SEGMENTS+1];
 };
 
 struct virtblk_req
@@ -72,7 +74,8 @@ static bool do_req(struct request_queue 
 static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
 		   struct request *req)
 {
-	unsigned long num, out, in;
+	unsigned long num;
+	struct scatterlist *in_sg;
 	struct virtblk_req *vbr;
 
 	vbr = mempool_alloc(vblk->pool, GFP_ATOMIC);
@@ -98,22 +101,24 @@ static bool do_req(struct request_queue 
 		vbr->out_hdr.type |= VIRTIO_BLK_T_BARRIER;
 
 	/* This init could be done at vblk creation time */
-	sg_init_table(vblk->sg, VIRTIO_MAX_SG);
-	sg_set_buf(&vblk->sg[0], &vbr->out_hdr, sizeof(vbr->out_hdr));
-	num = blk_rq_map_sg(q, vbr->req, vblk->sg+1);
-	sg_set_buf(&vblk->sg[num+1], &vbr->in_hdr, sizeof(vbr->in_hdr));
+	sg_init_table(vblk->req_sg, VIRTIO_MAX_SG);
+	num = blk_rq_map_sg(q, vbr->req, vblk->req_sg);
+	sg_init_one(&vblk->out_sg[0], &vbr->out_hdr, sizeof(vbr->out_hdr));
+	sg_init_one(&vblk->in_sg[0], &vbr->in_hdr, sizeof(vbr->in_hdr));
 
 	if (rq_data_dir(vbr->req) == WRITE) {
 		vbr->out_hdr.type |= VIRTIO_BLK_T_OUT;
-		out = 1 + num;
-		in = 1;
+		/* Chain write request onto output buffers. */
+		sg_chain(vblk->out_sg, 2, vblk->req_sg);
+		in_sg = vblk->in_sg;
 	} else {
 		vbr->out_hdr.type |= VIRTIO_BLK_T_IN;
-		out = 1;
-		in = 1 + num;
+		/* Chain input (status) buffer at end of read buffers. */
+		sg_chain(vblk->req_sg, num+1, vblk->in_sg);
+		in_sg = vblk->req_sg;
 	}
 
-	if (vblk->vq->vq_ops->add_buf(vblk->vq, vblk->sg, out, in, vbr)) {
+	if (vblk->vq->vq_ops->add_buf(vblk->vq, vblk->out_sg, in_sg, vbr)) {
 		mempool_free(vbr, vblk->pool);
 		return false;
 	}
@@ -130,7 +135,7 @@ static void do_virtblk_request(struct re
 
 	while ((req = elv_next_request(q)) != NULL) {
 		vblk = req->rq_disk->private_data;
-		BUG_ON(req->nr_phys_segments > ARRAY_SIZE(vblk->sg));
+		BUG_ON(req->nr_phys_segments > ARRAY_SIZE(vblk->req_sg)-1);
 
 		/* If this request fails, stop queue and wait for something to
 		   finish to restart it. */
diff -r 913cb29cde3c drivers/char/virtio_console.c
--- a/drivers/char/virtio_console.c	Mon Jan 07 13:55:26 2008 +1100
+++ b/drivers/char/virtio_console.c	Mon Jan 07 14:26:10 2008 +1100
@@ -62,7 +62,7 @@ static int put_chars(u32 vtermno, const 
 
 	/* add_buf wants a token to identify this buffer: we hand it any
 	 * non-NULL pointer, since there's only ever one buffer. */
-	if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) == 0) {
+	if (out_vq->vq_ops->add_buf(out_vq, sg, NULL, (void *)1) == 0) {
 		/* Tell Host to go! */
 		out_vq->vq_ops->kick(out_vq);
 		/* Chill out until it's done with the buffer. */
@@ -82,7 +82,7 @@ static void add_inbuf(void)
 	sg_init_one(sg, inbuf, PAGE_SIZE);
 
 	/* We should always be able to add one buffer to an empty queue. */
-	if (in_vq->vq_ops->add_buf(in_vq, sg, 0, 1, inbuf) != 0)
+	if (in_vq->vq_ops->add_buf(in_vq, NULL, sg, inbuf) != 0)
 		BUG();
 	in_vq->vq_ops->kick(in_vq);
 }
diff -r 913cb29cde3c drivers/net/virtio_net.c
--- a/drivers/net/virtio_net.c	Mon Jan 07 13:55:26 2008 +1100
+++ b/drivers/net/virtio_net.c	Mon Jan 07 14:26:10 2008 +1100
@@ -183,21 +183,25 @@ static void try_fill_recv(struct virtnet
 static void try_fill_recv(struct virtnet_info *vi)
 {
 	struct sk_buff *skb;
-	struct scatterlist sg[1+MAX_SKB_FRAGS];
-	int num, err;
+	/* +1 for chaining. */
+	struct scatterlist hdr_sg[2];
+	struct scatterlist skb_sg[MAX_SKB_FRAGS];
+	int err;
 
-	sg_init_table(sg, 1+MAX_SKB_FRAGS);
+	sg_init_table(skb_sg, MAX_SKB_FRAGS);
 	for (;;) {
 		skb = netdev_alloc_skb(vi->dev, MAX_PACKET_LEN);
 		if (unlikely(!skb))
 			break;
 
 		skb_put(skb, MAX_PACKET_LEN);
-		vnet_hdr_to_sg(sg, skb);
-		num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1;
+		vnet_hdr_to_sg(&hdr_sg[0], skb);
+		skb_to_sgvec(skb, skb_sg, 0, skb->len);
 		skb_queue_head(&vi->recv, skb);
 
-		err = vi->rvq->vq_ops->add_buf(vi->rvq, sg, 0, num, skb);
+		sg_chain(hdr_sg, 2, skb_sg);
+
+		err = vi->rvq->vq_ops->add_buf(vi->rvq, NULL, hdr_sg, skb);
 		if (err) {
 			skb_unlink(skb, &vi->recv);
 			kfree_skb(skb);
@@ -266,13 +270,15 @@ static int start_xmit(struct sk_buff *sk
 static int start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct virtnet_info *vi = netdev_priv(dev);
-	int num, err;
-	struct scatterlist sg[1+MAX_SKB_FRAGS];
+	int err;
+	/* +1 for chaining. */
+	struct scatterlist hdr_sg[2];
+	struct scatterlist skb_sg[MAX_SKB_FRAGS];
 	struct virtio_net_hdr *hdr;
 	const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest;
 	DECLARE_MAC_BUF(mac);
 
-	sg_init_table(sg, 1+MAX_SKB_FRAGS);
+	sg_init_table(skb_sg, MAX_SKB_FRAGS);
 
 	pr_debug("%s: xmit %p %s\n", dev->name, skb, print_mac(mac, dest));
 
@@ -304,12 +310,13 @@ static int start_xmit(struct sk_buff *sk
 		hdr->gso_size = 0;
 	}
 
-	vnet_hdr_to_sg(sg, skb);
-	num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1;
+	vnet_hdr_to_sg(&hdr_sg[0], skb);
+	skb_to_sgvec(skb, skb_sg, 0, skb->len);
 	__skb_queue_head(&vi->send, skb);
+	sg_chain(hdr_sg, 2, skb_sg);
 
 again:
-	err = vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb);
+	err = vi->svq->vq_ops->add_buf(vi->svq, hdr_sg, NULL, skb);
 	if (err) {
 		/* Can we free any used skbs? */
 		if (free_old_xmit_skbs(vi))
