dccp_recv: Use para_readv() instead of recv_bin_buffer().
authorAndre Noll <maan@systemlinux.org>
Sat, 13 Feb 2010 12:12:47 +0000 (13:12 +0100)
committerAndre Noll <maan@systemlinux.org>
Sat, 13 Feb 2010 12:12:47 +0000 (13:12 +0100)
When the end of the buffer pool area is reached, the old code ended up calling recv()
with a buffer size smaller than a full datagram. This results in data being lost
because the remaining part of the datagram will be discarded rather than returned
at the next call to recv().

By using the new btr_pool_get_buffers() we obtain two buffers in this case, a small
one pointing to the end of the area and a larger one pointing to the area start. Passing
both buffers to para_readv() instead of using recv_bin_buffer() receives the full
datagram and therefore avoids the above mentioned loss of data.

dccp_recv.c

index 0fddcfa..e0d3e23 100644 (file)
@@ -111,9 +111,8 @@ static void dccp_recv_post_select(struct sched *s, struct task *t)
        struct receiver_node *rn = container_of(t, struct receiver_node, task);
        struct private_dccp_recv_data *pdd = rn->private_data;
        struct btr_node *btrn = rn->btrn;
-       int ret;
-       char *buf;
-       size_t sz;
+       struct iovec iov[2];
+       int ret, iovcnt;
 
        ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
        if (ret < 0)
@@ -122,16 +121,21 @@ static void dccp_recv_post_select(struct sched *s, struct task *t)
                return;
        if (!FD_ISSET(pdd->fd, &s->rfds))
                return; /* nothing to do */
+       iovcnt = btr_pool_get_buffers(pdd->btrp, iov);
        ret = -E_DCCP_OVERRUN;
-       sz = btr_pool_get_buffer(pdd->btrp, &buf);
-       if (sz == 0)
+       if (iovcnt == 0)
                goto err;
-       ret = recv_bin_buffer(pdd->fd, buf, sz);
+       ret = para_readv(pdd->fd, iov, iovcnt);
        if (ret == 0)
                ret = -E_RECV_EOF;
        if (ret < 0)
                goto err;
-       btr_add_output_pool(pdd->btrp, ret, btrn);
+       if (ret <= iov[0].iov_len) /* only the first buffer was filled */
+               btr_add_output_pool(pdd->btrp, ret, btrn);
+       else { /* both buffers contain data */
+               btr_add_output_pool(pdd->btrp, iov[0].iov_len, btrn);
+               btr_add_output_pool(pdd->btrp, ret - iov[0].iov_len, btrn);
+       }
        return;
 err:
        btr_remove_node(rn->btrn);