MAJOR: connection: split the send call into connection and stream interface
Similar to what was done on the receive path, the data layer now provides
only an snd_buf() callback that is iterated over by the stream interface's
si_conn_send_loop() function.
The data layer now has no knowledge about channels nor stream interfaces.
The splice() code still need to be ported as it currently is disabled.
diff --git a/include/proto/connection.h b/include/proto/connection.h
index d97978e..7205b2c 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -37,16 +37,6 @@
conn->data->close(conn);
}
-/* Calls the snd_buf() function of the data layer if any, otherwise
- * returns 0.
- */
-static inline int conn_data_snd_buf(struct connection *conn)
-{
- if (!conn->data->snd_buf)
- return 0;
- return conn->data->snd_buf(conn);
-}
-
/* set polling depending on the change between the CURR part of the
* flags and the new flags in connection C. The connection flags are
* updated with the new flags at the end of the operation. Only the bits
diff --git a/include/types/stream_interface.h b/include/types/stream_interface.h
index 018349b..ee552c2 100644
--- a/include/types/stream_interface.h
+++ b/include/types/stream_interface.h
@@ -118,8 +118,8 @@
void (*read)(struct connection *conn); /* read callback after poll() */
void (*write)(struct connection *conn); /* write callback after poll() */
void (*close)(struct connection *); /* close the data channel on the connection */
- int (*snd_buf)(struct connection *conn); /* callback used to send a buffer contents */
int (*rcv_buf)(struct connection *conn, struct buffer *buf, int count); /* recv callback */
+ int (*snd_buf)(struct connection *conn, struct buffer *buf, int flags); /* send callback */
};
/* A stream interface has 3 parts :
diff --git a/src/raw_sock.c b/src/raw_sock.c
index 1bc4042..60c7ed0 100644
--- a/src/raw_sock.c
+++ b/src/raw_sock.c
@@ -279,148 +279,60 @@
}
-/*
- * This function is called to send buffer data to a stream socket.
- * It returns -1 in case of unrecoverable error, otherwise zero.
+/* Send all pending bytes from buffer <buf> to connection <conn>'s socket.
+ * <flags> may contain MSG_MORE to make the system hold on without sending
+ * data too fast.
+ * Only one call to send() is performed, unless the buffer wraps, in which case
+ * a second call may be performed. The connection's flags are updated with
+ * whatever special event is detected (error, empty). The caller is responsible
+ * for taking care of those events and avoiding the call if inappropriate. The
+ * function does not call the connection's polling update function, so the caller
+ * is responsible for this.
*/
-static int sock_raw_write_loop(struct connection *conn)
+static int raw_sock_from_buf(struct connection *conn, struct buffer *buf, int flags)
{
- struct stream_interface *si = container_of(conn, struct stream_interface, conn);
- struct channel *b = si->ob;
- int write_poll = MAX_WRITE_POLL_LOOPS;
- int ret, max;
+ int ret, try, done, send_flag;
-#if 0 && defined(CONFIG_HAP_LINUX_SPLICE)
- while (b->pipe) {
- ret = splice(b->pipe->cons, NULL, si_fd(si), NULL, b->pipe->data,
- SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
- if (ret <= 0) {
- if (ret == 0 || errno == EAGAIN) {
- conn_data_poll_send(&si->conn);
- return 0;
- }
- /* here we have another error */
- return -1;
- }
-
- b->flags |= BF_WRITE_PARTIAL;
- b->pipe->data -= ret;
-
- if (!b->pipe->data) {
- put_pipe(b->pipe);
- b->pipe = NULL;
- break;
- }
-
- if (--write_poll <= 0)
- return 0;
-
- /* The only reason we did not empty the pipe is that the output
- * buffer is full.
- */
- conn_data_poll_send(&si->conn);
- return 0;
- }
-
- /* At this point, the pipe is empty, but we may still have data pending
- * in the normal buffer.
+ done = 0;
+ /* send the largest possible block. For this we perform only one call
+ * to send() unless the buffer wraps and we exactly fill the first hunk,
+ * in which case we accept to do it once again.
*/
-#endif
- if (!b->buf.o) {
- b->flags |= BF_OUT_EMPTY;
- return 0;
- }
-
- /* when we're in this loop, we already know that there is no spliced
- * data left, and that there are sendable buffered data.
- */
- while (1) {
- max = b->buf.o;
-
+ while (buf->o) {
+ try = buf->o;
/* outgoing data may wrap at the end */
- if (b->buf.data + max > b->buf.p)
- max = b->buf.data + max - b->buf.p;
+ if (buf->data + try > buf->p)
+ try = buf->data + try - buf->p;
- /* check if we want to inform the kernel that we're interested in
- * sending more data after this call. We want this if :
- * - we're about to close after this last send and want to merge
- * the ongoing FIN with the last segment.
- * - we know we can't send everything at once and must get back
- * here because of unaligned data
- * - there is still a finite amount of data to forward
- * The test is arranged so that the most common case does only 2
- * tests.
- */
+ send_flag = MSG_DONTWAIT | MSG_NOSIGNAL;
+ if (try < buf->o)
+ send_flag = MSG_MORE;
- if (MSG_NOSIGNAL && MSG_MORE) {
- unsigned int send_flag = MSG_DONTWAIT | MSG_NOSIGNAL;
-
- if ((!(b->flags & BF_NEVER_WAIT) &&
- ((b->to_forward && b->to_forward != BUF_INFINITE_FORWARD) ||
- (b->flags & BF_EXPECT_MORE))) ||
- ((b->flags & (BF_SHUTW|BF_SHUTW_NOW|BF_HIJACK)) == BF_SHUTW_NOW && (max == b->buf.o)) ||
- (max != b->buf.o)) {
- send_flag |= MSG_MORE;
- }
-
- /* this flag has precedence over the rest */
- if (b->flags & BF_SEND_DONTWAIT)
- send_flag &= ~MSG_MORE;
-
- ret = send(si_fd(si), bo_ptr(&b->buf), max, send_flag);
- } else {
- int skerr;
- socklen_t lskerr = sizeof(skerr);
-
- ret = getsockopt(si_fd(si), SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
- if (ret == -1 || skerr)
- ret = -1;
- else
- ret = send(si_fd(si), bo_ptr(&b->buf), max, MSG_DONTWAIT);
- }
+ ret = send(conn->t.sock.fd, bo_ptr(buf), try, send_flag | flags);
if (ret > 0) {
- if (si->conn.flags & CO_FL_WAIT_L4_CONN) {
- si->conn.flags &= ~CO_FL_WAIT_L4_CONN;
- si->exp = TICK_ETERNITY;
- }
+ buf->o -= ret;
+ done += ret;
- b->flags |= BF_WRITE_PARTIAL;
-
- b->buf.o -= ret;
- if (likely(!buffer_len(&b->buf)))
+ if (likely(!buffer_len(buf)))
/* optimize data alignment in the buffer */
- b->buf.p = b->buf.data;
-
- if (likely(!bi_full(b)))
- b->flags &= ~BF_FULL;
-
- if (!b->buf.o) {
- /* Always clear both flags once everything has been sent, they're one-shot */
- b->flags &= ~(BF_EXPECT_MORE | BF_SEND_DONTWAIT);
- if (likely(!b->pipe))
- b->flags |= BF_OUT_EMPTY;
- break;
- }
+ buf->p = buf->data;
/* if the system buffer is full, don't insist */
- if (ret < max)
- break;
-
- if (--write_poll <= 0)
+ if (ret < try)
break;
}
else if (ret == 0 || errno == EAGAIN) {
/* nothing written, we need to poll for write first */
- conn_data_poll_send(&si->conn);
- return 0;
+ conn->flags |= CO_FL_WAIT_ROOM;
+ break;
}
- else {
- /* bad, we got an error */
- return -1;
+ else if (errno != EINTR) {
+ conn->flags |= CO_FL_ERROR;
+ break;
}
- } /* while (1) */
- return 0;
+ }
+ return done;
}
@@ -433,7 +345,7 @@
.chk_snd = stream_int_chk_snd_conn,
.read = si_conn_recv_cb,
.write = si_conn_send_cb,
- .snd_buf = sock_raw_write_loop,
+ .snd_buf = raw_sock_from_buf,
.rcv_buf = raw_sock_to_buf,
.close = NULL,
};
diff --git a/src/stream_interface.c b/src/stream_interface.c
index 2cc1962..eef6dec 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -654,6 +654,118 @@
si->ib->flags &= ~BF_READ_DONTWAIT;
}
+/*
+ * This function is called to send buffer data to a stream socket.
+ * It returns -1 in case of unrecoverable error, otherwise zero.
+ * It iterates the data layer's snd_buf function.
+ */
+static int si_conn_send_loop(struct connection *conn)
+{
+ struct stream_interface *si = container_of(conn, struct stream_interface, conn);
+ struct channel *b = si->ob;
+ int write_poll = MAX_WRITE_POLL_LOOPS;
+ int ret;
+
+#if 0 && defined(CONFIG_HAP_LINUX_SPLICE)
+ while (b->pipe) {
+ ret = splice(b->pipe->cons, NULL, si_fd(si), NULL, b->pipe->data,
+ SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
+ if (ret <= 0) {
+ if (ret == 0 || errno == EAGAIN) {
+ conn_data_poll_send(&si->conn);
+ return 0;
+ }
+ /* here we have another error */
+ return -1;
+ }
+
+ b->flags |= BF_WRITE_PARTIAL;
+ b->pipe->data -= ret;
+
+ if (!b->pipe->data) {
+ put_pipe(b->pipe);
+ b->pipe = NULL;
+ break;
+ }
+
+ if (--write_poll <= 0)
+ return 0;
+
+ /* The only reason we did not empty the pipe is that the output
+ * buffer is full.
+ */
+ conn_data_poll_send(&si->conn);
+ return 0;
+ }
+
+ /* At this point, the pipe is empty, but we may still have data pending
+ * in the normal buffer.
+ */
+#endif
+ if (!b->buf.o) {
+ b->flags |= BF_OUT_EMPTY;
+ return 0;
+ }
+
+ /* when we're in this loop, we already know that there is no spliced
+ * data left, and that there are sendable buffered data.
+ */
+ conn->flags &= ~(CO_FL_WAIT_DATA | CO_FL_WAIT_ROOM);
+ while (!(conn->flags & (CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_DATA_WR_SH | CO_FL_WAIT_DATA | CO_FL_WAIT_ROOM | CO_FL_HANDSHAKE))) {
+ /* check if we want to inform the kernel that we're interested in
+ * sending more data after this call. We want this if :
+ * - we're about to close after this last send and want to merge
+ * the ongoing FIN with the last segment.
+ * - we know we can't send everything at once and must get back
+ * here because of unaligned data
+ * - there is still a finite amount of data to forward
+ * The test is arranged so that the most common case does only 2
+ * tests.
+ */
+ unsigned int send_flag = MSG_DONTWAIT | MSG_NOSIGNAL;
+
+ if ((!(b->flags & (BF_NEVER_WAIT|BF_SEND_DONTWAIT)) &&
+ ((b->to_forward && b->to_forward != BUF_INFINITE_FORWARD) ||
+ (b->flags & BF_EXPECT_MORE))) ||
+ ((b->flags & (BF_SHUTW|BF_SHUTW_NOW|BF_HIJACK)) == BF_SHUTW_NOW))
+ send_flag |= MSG_MORE;
+
+ ret = conn->data->snd_buf(conn, &b->buf, send_flag);
+ if (ret <= 0)
+ break;
+
+ if (si->conn.flags & CO_FL_WAIT_L4_CONN)
+ si->conn.flags &= ~CO_FL_WAIT_L4_CONN;
+
+ b->flags |= BF_WRITE_PARTIAL;
+
+ if (likely(!bi_full(b)))
+ b->flags &= ~BF_FULL;
+
+ if (!b->buf.o) {
+ /* Always clear both flags once everything has been sent, they're one-shot */
+ b->flags &= ~(BF_EXPECT_MORE | BF_SEND_DONTWAIT);
+ if (likely(!b->pipe))
+ b->flags |= BF_OUT_EMPTY;
+ break;
+ }
+
+ if (--write_poll <= 0)
+ break;
+ } /* while */
+
+ if (conn->flags & CO_FL_ERROR)
+ return -1;
+
+ if (conn->flags & CO_FL_WAIT_ROOM) {
+ /* we need to poll before going on */
+ conn_data_poll_send(&si->conn);
+ return 0;
+ }
+ return 0;
+}
+
+
/* Updates the timers and flags of a stream interface attached to a connection,
* depending on the buffers' flags. It should only be called once after the
* buffer flags have settled down, and before they are cleared. It doesn't
@@ -792,7 +904,7 @@
(fdtab[si_fd(si)].ev & FD_POLL_OUT))) /* we'll be called anyway */
return;
- if (conn_data_snd_buf(&si->conn) < 0) {
+ if (si_conn_send_loop(&si->conn) < 0) {
/* Write error on the file descriptor. We mark the FD as STERROR so
* that we don't use it anymore and we notify the task.
*/
@@ -1078,7 +1190,7 @@
return;
/* OK there are data waiting to be sent */
- if (conn_data_snd_buf(conn) < 0)
+ if (si_conn_send_loop(conn) < 0)
goto out_error;
/* OK all done */