#include "opt_param.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/domain.h>
#include <sys/file.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/resourcevar.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/thread2.h>
#include <sys/msgport2.h>
void
sbappend(struct sockbuf *sb, struct mbuf *m)
{
struct mbuf *n;
mbuftrackid(m, 16);
if (m) {
n = sb->sb_lastrecord;
if (n) {
if (n->m_flags & M_EOR) {
sbappendrecord(sb, m);
return;
}
}
n = sb->sb_lastmbuf;
if (n) {
if (n->m_flags & M_EOR) {
sbappendrecord(sb, m);
return;
}
}
sbcompress(sb, m, n);
}
}
void
sbappendstream(struct sockbuf *sb, struct mbuf *m)
{
mbuftrackid(m, 17);
KKASSERT(m->m_nextpkt == NULL);
sbcompress(sb, m, sb->sb_lastmbuf);
}
#ifdef SOCKBUF_DEBUG
void
_sbcheck(struct sockbuf *sb)
{
struct mbuf *m;
struct mbuf *n = NULL;
u_long len = 0, mbcnt = 0;
for (m = sb->sb_mb; m; m = n) {
n = m->m_nextpkt;
if (n == NULL && sb->sb_lastrecord != m) {
kprintf("sockbuf %p mismatched lastrecord %p vs %p\n", sb, sb->sb_lastrecord, m);
panic("sbcheck1");
}
for (; m; m = m->m_next) {
len += m->m_len;
mbcnt += MSIZE;
if (m->m_flags & M_EXT)
mbcnt += m->m_ext.ext_size;
if (n == NULL && m->m_next == NULL) {
if (sb->sb_lastmbuf != m) {
kprintf("sockbuf %p mismatched lastmbuf %p vs %p\n", sb, sb->sb_lastmbuf, m);
panic("sbcheck2");
}
}
}
}
if (sb->sb_mb == NULL) {
if (sb->sb_lastrecord != NULL) {
kprintf("sockbuf %p is empty, lastrecord not NULL: %p\n",
sb, sb->sb_lastrecord);
panic("sbcheck3");
}
if (sb->sb_lastmbuf != NULL) {
kprintf("sockbuf %p is empty, lastmbuf not NULL: %p\n",
sb, sb->sb_lastmbuf);
panic("sbcheck4");
}
}
if (len != sb->sb_cc || mbcnt != sb->sb_mbcnt) {
kprintf("sockbuf %p cc %ld != %ld || mbcnt %ld != %ld\n",
sb, len, sb->sb_cc, mbcnt, sb->sb_mbcnt);
panic("sbcheck5");
}
}
#endif
void
sbappendrecord(struct sockbuf *sb, struct mbuf *m0)
{
struct mbuf *firstmbuf;
struct mbuf *secondmbuf;
if (m0 == NULL)
return;
mbuftrackid(m0, 18);
sbcheck(sb);
firstmbuf = m0;
secondmbuf = m0->m_next;
m0->m_next = NULL;
if (sb->sb_mb == NULL)
sb->sb_mb = firstmbuf;
else
sb->sb_lastrecord->m_nextpkt = firstmbuf;
sb->sb_lastrecord = firstmbuf;
sb->sb_lastmbuf = firstmbuf;
if ((firstmbuf->m_flags & M_EOR) && (secondmbuf != NULL)) {
firstmbuf->m_flags &= ~M_EOR;
secondmbuf->m_flags |= M_EOR;
}
sballoc(sb, firstmbuf);
sbcompress(sb, secondmbuf, firstmbuf);
}
int
sbappendaddr(struct sockbuf *sb, const struct sockaddr *asa, struct mbuf *m0,
struct mbuf *control)
{
struct mbuf *m, *n;
int eor;
mbuftrackid(m0, 19);
mbuftrackid(control, 20);
if (m0 && (m0->m_flags & M_PKTHDR) == 0)
panic("sbappendaddr");
sbcheck(sb);
for (n = control; n; n = n->m_next) {
if (n->m_next == NULL)
break;
}
if (asa->sa_len > MLEN)
return (0);
MGET(m, M_NOWAIT, MT_SONAME);
if (m == NULL)
return (0);
KKASSERT(m->m_nextpkt == NULL);
m->m_len = asa->sa_len;
bcopy(asa, mtod(m, caddr_t), asa->sa_len);
if (n)
n->m_next = m0;
else
control = m0;
m->m_next = control;
for (n = m; n; n = n->m_next)
sballoc(sb, n);
if (sb->sb_mb == NULL)
sb->sb_mb = m;
else
sb->sb_lastrecord->m_nextpkt = m;
sb->sb_lastrecord = m;
eor = m->m_flags;
while (m->m_next) {
m->m_flags &= ~M_EOR;
m = m->m_next;
eor |= m->m_flags;
}
m->m_flags |= eor & M_EOR;
sb->sb_lastmbuf = m;
return (1);
}
int
sbappendcontrol(struct sockbuf *sb, struct mbuf *m0, struct mbuf *control)
{
struct mbuf *n;
u_int length, cmbcnt, m0mbcnt;
int eor;
KASSERT(control != NULL, ("sbappendcontrol"));
KKASSERT(control->m_nextpkt == NULL);
sbcheck(sb);
mbuftrackid(m0, 21);
mbuftrackid(control, 22);
length = m_countm(control, &n, &cmbcnt) + m_countm(m0, NULL, &m0mbcnt);
KKASSERT(m0 != NULL);
n->m_next = m0;
if (sb->sb_mb == NULL)
sb->sb_mb = control;
else
sb->sb_lastrecord->m_nextpkt = control;
sb->sb_lastrecord = control;
eor = m0->m_flags;
while (m0->m_next) {
m0->m_flags &= ~M_EOR;
m0 = m0->m_next;
eor |= m0->m_flags;
}
m0->m_flags |= eor & M_EOR;
sb->sb_lastmbuf = m0;
sb->sb_cc += length;
sb->sb_mbcnt += cmbcnt + m0mbcnt;
return (1);
}
void
sbcompress(struct sockbuf *sb, struct mbuf *m, struct mbuf *tailm)
{
int eor = 0;
struct mbuf *free_chain = NULL;
mbuftrackid(m, 23);
sbcheck(sb);
while (m) {
struct mbuf *o;
eor |= m->m_flags & M_EOR;
if (m->m_len == 0 &&
(eor == 0 ||
(((o = m->m_next) || (o = tailm)) &&
o->m_type == m->m_type))) {
o = m->m_next;
m->m_next = free_chain;
free_chain = m;
m = o;
continue;
}
if (tailm && !(tailm->m_flags & (M_EOR | M_SOLOCKED)) &&
M_WRITABLE(tailm) &&
m->m_len <= MCLBYTES / 4 &&
m->m_len <= M_TRAILINGSPACE(tailm) &&
tailm->m_type == m->m_type) {
u_long mbcnt_sz;
bcopy(mtod(m, caddr_t),
mtod(tailm, caddr_t) + tailm->m_len,
(unsigned)m->m_len);
tailm->m_len += m->m_len;
sb->sb_cc += m->m_len;
mbcnt_sz = MSIZE;
if (m->m_flags & M_EXT)
mbcnt_sz += m->m_ext.ext_size;
atomic_subtract_long(&sb->sb_mbcnt_prealloc, mbcnt_sz);
o = m->m_next;
m->m_next = free_chain;
free_chain = m;
m = o;
continue;
}
if (tailm == NULL) {
KASSERT(sb->sb_mb == NULL,
("sbcompress: sb_mb not NULL"));
sb->sb_mb = m;
sb->sb_lastrecord = m;
} else {
tailm->m_next = m;
}
sb->sb_lastmbuf = m;
tailm = m;
m = m->m_next;
tailm->m_next = NULL;
sballoc(sb, tailm);
tailm->m_flags &= ~M_EOR;
}
if (eor) {
if (tailm)
tailm->m_flags |= eor;
else
kprintf("semi-panic: sbcompress");
}
while (free_chain)
free_chain = m_free(free_chain);
sbcheck(sb);
}
void
sbflush(struct sockbuf *sb)
{
while (sb->sb_mbcnt) {
if (!sb->sb_cc && (sb->sb_mb == NULL || sb->sb_mb->m_len))
break;
sbdrop(sb, (int)sb->sb_cc);
}
KASSERT(!(sb->sb_cc || sb->sb_mb || sb->sb_mbcnt || sb->sb_lastmbuf),
("sbflush: cc %ld || mb %p || mbcnt %ld || lastmbuf %p",
sb->sb_cc, sb->sb_mb, sb->sb_mbcnt, sb->sb_lastmbuf));
}
void
sbdrop(struct sockbuf *sb, int len)
{
struct mbuf *m;
struct mbuf *free_chain = NULL;
sbcheck(sb);
crit_enter();
m = sb->sb_mb;
while (m && len > 0) {
if (m->m_len > len) {
m->m_len -= len;
m->m_data += len;
sb->sb_cc -= len;
atomic_subtract_long(&sb->sb_cc_prealloc, len);
break;
}
len -= m->m_len;
m = sbunlinkmbuf(sb, m, &free_chain);
if (m == NULL && len)
m = sb->sb_mb;
}
while (m && m->m_len == 0) {
m = sbunlinkmbuf(sb, m, &free_chain);
}
crit_exit();
if (free_chain)
m_freem(free_chain);
sbcheck(sb);
}
void
sbdroprecord(struct sockbuf *sb)
{
struct mbuf *m;
struct mbuf *n;
sbcheck(sb);
m = sb->sb_mb;
if (m) {
if ((sb->sb_mb = m->m_nextpkt) == NULL) {
sb->sb_lastrecord = NULL;
sb->sb_lastmbuf = NULL;
}
m->m_nextpkt = NULL;
for (n = m; n; n = n->m_next)
sbfree(sb, n);
m_freem(m);
sbcheck(sb);
}
}
struct mbuf *
sbunlinkmbuf(struct sockbuf *sb, struct mbuf *m, struct mbuf **free_chain)
{
struct mbuf *n;
KKASSERT(sb->sb_mb == m);
sbfree(sb, m);
n = m->m_next;
if (n) {
sb->sb_mb = n;
if (sb->sb_lastrecord == m)
sb->sb_lastrecord = n;
KKASSERT(sb->sb_lastmbuf != m);
n->m_nextpkt = m->m_nextpkt;
} else {
sb->sb_mb = m->m_nextpkt;
if (sb->sb_lastrecord == m) {
KKASSERT(sb->sb_mb == NULL);
sb->sb_lastrecord = NULL;
}
if (sb->sb_mb == NULL)
sb->sb_lastmbuf = NULL;
}
m->m_nextpkt = NULL;
if (free_chain) {
m->m_next = *free_chain;
*free_chain = m;
} else {
m->m_next = NULL;
}
return(n);
}
struct mbuf *
sbcreatecontrol(const void *p, size_t size, int type, int level)
{
struct cmsghdr *cp;
struct mbuf *m;
if (CMSG_SPACE(size) > MCLBYTES)
return (NULL);
m = m_getl(CMSG_SPACE(size), M_NOWAIT, MT_CONTROL, 0, NULL);
if (m == NULL)
return (NULL);
m->m_len = CMSG_SPACE(size);
cp = mtod(m, struct cmsghdr *);
if (p != NULL)
memcpy(CMSG_DATA(cp), p, size);
cp->cmsg_len = CMSG_LEN(size);
cp->cmsg_level = level;
cp->cmsg_type = type;
mbuftrackid(m, 24);
return (m);
}