diff options
author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 |
---|---|---|
committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 |
commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
tree | 65ca85f13617aee1dce474596800950f266a456c /slirp/sbuf.c | |
download | qemu-3f2546b2ef55b661fd8dd69682b38992225e86f6.tar.gz qemu-3f2546b2ef55b661fd8dd69682b38992225e86f6.tar.bz2 qemu-3f2546b2ef55b661fd8dd69682b38992225e86f6.zip |
Diffstat (limited to 'slirp/sbuf.c')
-rw-r--r-- | slirp/sbuf.c | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/slirp/sbuf.c b/slirp/sbuf.c new file mode 100644 index 00000000..08ec2b4f --- /dev/null +++ b/slirp/sbuf.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 1995 Danny Gasparovski. + * + * Please read the file COPYRIGHT for the + * terms and conditions of the copyright. + */ + +#include <slirp.h> +#include <qemu/main-loop.h> + +static void sbappendsb(struct sbuf *sb, struct mbuf *m); + +void +sbfree(struct sbuf *sb) +{ + free(sb->sb_data); +} + +void +sbdrop(struct sbuf *sb, int num) +{ + int limit = sb->sb_datalen / 2; + + /* + * We can only drop how much we have + * This should never succeed + */ + if(num > sb->sb_cc) + num = sb->sb_cc; + sb->sb_cc -= num; + sb->sb_rptr += num; + if(sb->sb_rptr >= sb->sb_data + sb->sb_datalen) + sb->sb_rptr -= sb->sb_datalen; + + if (sb->sb_cc < limit && sb->sb_cc + num >= limit) { + qemu_notify_event(); + } +} + +void +sbreserve(struct sbuf *sb, int size) +{ + if (sb->sb_data) { + /* Already alloced, realloc if necessary */ + if (sb->sb_datalen != size) { + sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)realloc(sb->sb_data, size); + sb->sb_cc = 0; + if (sb->sb_wptr) + sb->sb_datalen = size; + else + sb->sb_datalen = 0; + } + } else { + sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)malloc(size); + sb->sb_cc = 0; + if (sb->sb_wptr) + sb->sb_datalen = size; + else + sb->sb_datalen = 0; + } +} + +/* + * Try and write() to the socket, whatever doesn't get written + * append to the buffer... for a host with a fast net connection, + * this prevents an unnecessary copy of the data + * (the socket is non-blocking, so we won't hang) + */ +void +sbappend(struct socket *so, struct mbuf *m) +{ + int ret = 0; + + DEBUG_CALL("sbappend"); + DEBUG_ARG("so = %lx", (long)so); + DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("m->m_len = %d", m->m_len); + + /* Shouldn't happen, but... e.g. foreign host closes connection */ + if (m->m_len <= 0) { + m_free(m); + return; + } + + /* + * If there is urgent data, call sosendoob + * if not all was sent, sowrite will take care of the rest + * (The rest of this function is just an optimisation) + */ + if (so->so_urgc) { + sbappendsb(&so->so_rcv, m); + m_free(m); + sosendoob(so); + return; + } + + /* + * We only write if there's nothing in the buffer, + * ottherwise it'll arrive out of order, and hence corrupt + */ + if (!so->so_rcv.sb_cc) + ret = slirp_send(so, m->m_data, m->m_len, 0); + + if (ret <= 0) { + /* + * Nothing was written + * It's possible that the socket has closed, but + * we don't need to check because if it has closed, + * it will be detected in the normal way by soread() + */ + sbappendsb(&so->so_rcv, m); + } else if (ret != m->m_len) { + /* + * Something was written, but not everything.. + * sbappendsb the rest + */ + m->m_len -= ret; + m->m_data += ret; + sbappendsb(&so->so_rcv, m); + } /* else */ + /* Whatever happened, we free the mbuf */ + m_free(m); +} + +/* + * Copy the data from m into sb + * The caller is responsible to make sure there's enough room + */ +static void +sbappendsb(struct sbuf *sb, struct mbuf *m) +{ + int len, n, nn; + + len = m->m_len; + + if (sb->sb_wptr < sb->sb_rptr) { + n = sb->sb_rptr - sb->sb_wptr; + if (n > len) n = len; + memcpy(sb->sb_wptr, m->m_data, n); + } else { + /* Do the right edge first */ + n = sb->sb_data + sb->sb_datalen - sb->sb_wptr; + if (n > len) n = len; + memcpy(sb->sb_wptr, m->m_data, n); + len -= n; + if (len) { + /* Now the left edge */ + nn = sb->sb_rptr - sb->sb_data; + if (nn > len) nn = len; + memcpy(sb->sb_data,m->m_data+n,nn); + n += nn; + } + } + + sb->sb_cc += n; + sb->sb_wptr += n; + if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen) + sb->sb_wptr -= sb->sb_datalen; +} + +/* + * Copy data from sbuf to a normal, straight buffer + * Don't update the sbuf rptr, this will be + * done in sbdrop when the data is acked + */ +void +sbcopy(struct sbuf *sb, int off, int len, char *to) +{ + char *from; + + from = sb->sb_rptr + off; + if (from >= sb->sb_data + sb->sb_datalen) + from -= sb->sb_datalen; + + if (from < sb->sb_wptr) { + if (len > sb->sb_cc) len = sb->sb_cc; + memcpy(to,from,len); + } else { + /* re-use off */ + off = (sb->sb_data + sb->sb_datalen) - from; + if (off > len) off = len; + memcpy(to,from,off); + len -= off; + if (len) + memcpy(to+off,sb->sb_data,len); + } +} |