Improve use of socket buffer upcalls.

Use soreadable()/sowriteable() in socket upcalls to avoid extra wakeups
until we have enough data to read or space to write.

Increase partial receive len from 1K to 128K to not wake up on every
received packet.

This significantly reduces locks congestion and CPU usage and improves
throughput for large I/Os on NICs without TSO and LRO.

Reviewed by:	trasz
Sponsored by:	iXsystems, Inc.
This commit is contained in:
Alexander Motin 2014-04-11 18:26:08 +00:00
parent e0f210e6ef
commit 35263d6a48

View File

@ -45,6 +45,7 @@
#include <sys/mbuf.h>
#include <sys/mutex.h>
#include <sys/module.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
@ -67,7 +68,7 @@ static int coalesce = 1;
TUNABLE_INT("kern.icl.coalesce", &coalesce);
SYSCTL_INT(_kern_icl, OID_AUTO, coalesce, CTLFLAG_RWTUN,
&coalesce, 1, "Try to coalesce PDUs before sending");
static int partial_receive_len = 1 * 1024; /* XXX: More? */
static int partial_receive_len = 128 * 1024;
TUNABLE_INT("kern.icl.partial_receive_len", &partial_receive_len);
SYSCTL_INT(_kern_icl, OID_AUTO, partial_receive_len, CTLFLAG_RWTUN,
&partial_receive_len, 1 * 1024, "Minimum read size for partially received "
@ -750,12 +751,19 @@ icl_receive_thread(void *arg)
break;
}
/*
* Set the low watermark, to be checked by
* soreadable() in icl_soupcall_receive()
* to avoid unneccessary wakeups until there
* is enough data received to read the PDU.
*/
SOCKBUF_LOCK(&so->so_rcv);
available = so->so_rcv.sb_cc;
if (available < ic->ic_receive_len) {
so->so_rcv.sb_lowat = ic->ic_receive_len;
cv_wait(&ic->ic_receive_cv, &so->so_rcv.sb_mtx);
}
} else
so->so_rcv.sb_lowat = so->so_rcv.sb_hiwat + 1;
SOCKBUF_UNLOCK(&so->so_rcv);
icl_conn_receive_pdus(ic, available);
@ -772,6 +780,9 @@ icl_soupcall_receive(struct socket *so, void *arg, int waitflag)
{
struct icl_conn *ic;
if (!soreadable(so))
return (SU_OK);
ic = arg;
cv_signal(&ic->ic_receive_cv);
return (SU_OK);
@ -854,10 +865,10 @@ icl_conn_send_pdus(struct icl_conn *ic, struct icl_pdu_stailq *queue)
available = sbspace(&so->so_snd);
/*
* Notify the socket layer that it doesn't need to call
* send socket upcall for the time being.
* Notify the socket upcall that we don't need wakeups
* for the time being.
*/
so->so_snd.sb_lowat = so->so_snd.sb_hiwat;
so->so_snd.sb_lowat = so->so_snd.sb_hiwat + 1;
SOCKBUF_UNLOCK(&so->so_snd);
while (!STAILQ_EMPTY(queue)) {
@ -873,7 +884,8 @@ icl_conn_send_pdus(struct icl_conn *ic, struct icl_pdu_stailq *queue)
#endif
/*
* Set the low watermark on the socket,
* Set the low watermark, to be checked by
* sowritable() in icl_soupcall_send()
* to avoid unneccessary wakeups until there
* is enough space for the PDU to fit.
*/
@ -1016,6 +1028,9 @@ icl_soupcall_send(struct socket *so, void *arg, int waitflag)
{
struct icl_conn *ic;
if (!sowriteable(so))
return (SU_OK);
ic = arg;
ICL_CONN_LOCK(ic);