From a5e9382686e31add8639d621f888ae44457bdda2 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Fri, 18 Jul 2014 09:32:46 -0700 Subject: [PATCH] kernel - Adjust ssb_space_prealloc() use cases * Add two flags to the signalsockbuf ssb_flags field. SSB_PREALLOC - Indicates that data preallocation tracking is being used SSB_STOPSUPP - Indicates that SSB_STOP flow control is being used * unix domain sockets set SSB_STOPSUPP, tcp and sctp sockets set SSB_PREALLOC. * sendfile() requires that either SSB_PREALLOC or SSB_STOPSUPP be specified. * Code now conditionalizes the use of ssb_space() vs ssb_space_prealloc() based on the presence of the SSB_PREALLOC flag. Reported-by: sephe --- sys/kern/uipc_socket2.c | 9 +++++++-- sys/kern/uipc_syscalls.c | 38 ++++++++++++++++++++++++++++++-------- sys/kern/uipc_usrreq.c | 12 +++++++++++- sys/netinet/sctp_usrreq.c | 3 +++ sys/netinet/tcp_usrreq.c | 4 ++-- sys/sys/socketvar.h | 2 ++ 6 files changed, 55 insertions(+), 13 deletions(-) diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c index 5342d5f6a2..f24ee15254 100644 --- a/sys/kern/uipc_socket2.c +++ b/sys/kern/uipc_socket2.c @@ -513,11 +513,16 @@ sowakeup(struct socket *so, struct signalsockbuf *ssb) * client side. */ for (;;) { + long space; + flags = ssb->ssb_flags; cpu_ccfence(); + if (ssb->ssb_flags & SSB_PREALLOC) + space = ssb_space_prealloc(ssb); + else + space = ssb_space(ssb); - if ((ssb == &so->so_snd && - ssb_space_prealloc(ssb) >= ssb->ssb_lowat) || + if ((ssb == &so->so_snd && space >= ssb->ssb_lowat) || (ssb == &so->so_rcv && ssb->ssb_cc >= ssb->ssb_lowat) || (ssb == &so->so_snd && (so->so_state & SS_CANTSENDMORE)) || (ssb == &so->so_rcv && (so->so_state & SS_CANTRCVMORE)) diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c index 502cbb74fa..028feffac1 100644 --- a/sys/kern/uipc_syscalls.c +++ b/sys/kern/uipc_syscalls.c @@ -1585,6 +1585,16 @@ kern_sendfile(struct vnode *vp, int sfd, off_t offset, size_t nbytes, goto done; } + /* + * preallocation is required for asynchronous passing of mbufs, + * otherwise we can wind up building up an infinite number of + * mbufs during the asynchronous latency. + */ + if ((so->so_snd.ssb_flags & (SSB_PREALLOC | SSB_STOPSUPP)) == 0) { + error = EINVAL; + goto done; + } + *sbytes = 0; /* * Protect against multiple writers to the socket. @@ -1600,7 +1610,7 @@ kern_sendfile(struct vnode *vp, int sfd, off_t offset, size_t nbytes, for (off = offset; ; off += xfsize, *sbytes += xfsize + hbytes) { vm_pindex_t pindex; vm_offset_t pgoff; - int space; + long space; pindex = OFF_TO_IDX(off); retry_lookup: @@ -1622,8 +1632,12 @@ retry_lookup: * Optimize the non-blocking case by looking at the socket space * before going to the extra work of constituting the sf_buf. */ - if ((fp->f_flag & FNONBLOCK) && - ssb_space_prealloc(&so->so_snd) <= 0) { + if (so->so_snd.ssb_flags & SSB_PREALLOC) + space = ssb_space_prealloc(&so->so_snd); + else + space = ssb_space(&so->so_snd); + + if ((fp->f_flag & FNONBLOCK) && space <= 0) { if (so->so_state & SS_CANTSENDMORE) error = EPIPE; else @@ -1803,7 +1817,11 @@ retry_space: * after checking the connection state above in order to avoid * a race condition with ssb_wait(). */ - space = ssb_space_prealloc(&so->so_snd); + if (so->so_snd.ssb_flags & SSB_PREALLOC) + space = ssb_space_prealloc(&so->so_snd); + else + space = ssb_space(&so->so_snd); + if (space < m->m_pkthdr.len && space < so->so_snd.ssb_lowat) { if (fp->f_flag & FNONBLOCK) { m_freem(m); @@ -1827,8 +1845,10 @@ retry_space: goto retry_space; } - for (mp = m; mp != NULL; mp = mp->m_next) - ssb_preallocstream(&so->so_snd, mp); + if (so->so_snd.ssb_flags & SSB_PREALLOC) { + for (mp = m; mp != NULL; mp = mp->m_next) + ssb_preallocstream(&so->so_snd, mp); + } if (use_sendfile_async) error = so_pru_senda(so, 0, m, NULL, NULL, td); else @@ -1843,8 +1863,10 @@ retry_space: if (mheader != NULL) { *sbytes += mheader->m_pkthdr.len; - for (mp = mheader; mp != NULL; mp = mp->m_next) - ssb_preallocstream(&so->so_snd, mp); + if (so->so_snd.ssb_flags & SSB_PREALLOC) { + for (mp = mheader; mp != NULL; mp = mp->m_next) + ssb_preallocstream(&so->so_snd, mp); + } if (use_sendfile_async) error = so_pru_senda(so, 0, mheader, NULL, NULL, td); else diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index cbe5acec9c..1129791e79 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -816,7 +816,6 @@ unp_attach(struct socket *so, struct pru_attach_info *ai) if (so->so_snd.ssb_hiwat == 0 || so->so_rcv.ssb_hiwat == 0) { switch (so->so_type) { - case SOCK_STREAM: case SOCK_SEQPACKET: error = soreserve(so, unpst_sendspace, unpst_recvspace, @@ -834,6 +833,17 @@ unp_attach(struct socket *so, struct pru_attach_info *ai) if (error) goto failed; } + + /* + * In order to support sendfile we have to set either SSB_STOPSUPP + * or SSB_PREALLOC. Unix domain sockets use the SSB_STOP flow + * control mechanism. + */ + if (so->so_type == SOCK_STREAM) { + atomic_set_int(&so->so_rcv.ssb_flags, SSB_STOPSUPP); + atomic_set_int(&so->so_snd.ssb_flags, SSB_STOPSUPP); + } + unp = kmalloc(sizeof(*unp), M_UNPCB, M_WAITOK | M_ZERO | M_NULLOK); if (unp == NULL) { error = ENOBUFS; diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index 436ff4aa2f..4aeee7dfe1 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -717,6 +717,9 @@ sctp_attach(netmsg_t msg) goto out; } error = soreserve(so, sctp_sendspace, sctp_recvspace, NULL); + atomic_set_int(&so->so_rcv.ssb_flags, SSB_PREALLOC); + atomic_set_int(&so->so_snd.ssb_flags, SSB_PREALLOC); + if (error) goto out; error = sctp_inpcb_alloc(so); diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index eeba2ba782..ebfcf872b7 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -1601,8 +1601,8 @@ tcp_attach(struct socket *so, struct pru_attach_info *ai) if (error) return (error); } - atomic_set_int(&so->so_rcv.ssb_flags, SSB_AUTOSIZE); - atomic_set_int(&so->so_snd.ssb_flags, SSB_AUTOSIZE); + atomic_set_int(&so->so_rcv.ssb_flags, SSB_AUTOSIZE | SSB_PREALLOC); + atomic_set_int(&so->so_snd.ssb_flags, SSB_AUTOSIZE | SSB_PREALLOC); cpu = mycpu->gd_cpuid; /* diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index 0c67fe4142..7461e45133 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -103,6 +103,8 @@ struct signalsockbuf { #define SSB_AUTOSIZE 0x0800 /* automatically size socket buffer */ #define SSB_AUTOLOWAT 0x1000 /* automatically scale lowat */ #define SSB_WAKEUP 0x2000 /* wakeup event race */ +#define SSB_PREALLOC 0x4000 /* prealloc supported */ +#define SSB_STOPSUPP 0x8000 /* SSB_STOP supported */ #define SSB_CLEAR_MASK (SSB_ASYNC | SSB_UPCALL | SSB_STOP | \ SSB_AUTOSIZE | SSB_AUTOLOWAT) -- 2.11.4.GIT