[MEDIUM] splice: set the capability on each stream_interface

The splice code did not consider compatibility between both ends
of the connection. Now we set different capabilities on each
stream interface, depending on what the protocol can splice to/from.
Right now, only TCP is supported. Thanks to this, we're now able to
automatically detect when splice() is not implemented and automatically
disable it on one end instead of reporting errors to the upper layer.
diff --git a/include/types/stream_interface.h b/include/types/stream_interface.h
index bb4a9e3..f97aa62 100644
--- a/include/types/stream_interface.h
+++ b/include/types/stream_interface.h
@@ -67,14 +67,17 @@
 	SI_FL_ERR        = 0x0002,  /* a non-recoverable error has occurred */
 	SI_FL_WAIT_ROOM  = 0x0004,  /* waiting for space to store incoming data */
 	SI_FL_WAIT_DATA  = 0x0008,  /* waiting for more data to send */
+	SI_FL_CAP_SPLTCP = 0x0010,  /* splicing possible from/to TCP */
 };
 
+#define SI_FL_CAP_SPLICE (SI_FL_CAP_SPLTCP)
+
 struct stream_interface {
 	unsigned int state;     /* SI_ST* */
 	unsigned int prev_state;/* SI_ST*, copy of previous state */
 	void *owner;            /* generally a (struct task*) */
 	int fd;                 /* file descriptor for a stream driver when known */
-	unsigned int flags;     /* SI_FL_*, must be cleared before I/O */
+	unsigned int flags;
 	unsigned int exp;       /* wake up time for connect, queue, turn-around, ... */
 	void (*shutr)(struct stream_interface *);  /* shutr function */
 	void (*shutw)(struct stream_interface *);  /* shutw function */
diff --git a/src/backend.c b/src/backend.c
index 5cea808..bf9f789 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1985,6 +1985,7 @@
 	EV_FD_SET(fd, DIR_WR);  /* for connect status */
 
 	s->req->cons->state = SI_ST_CON;
+	s->req->cons->flags |= SI_FL_CAP_SPLTCP; /* TCP supports splicing */
 	if (s->srv) {
 		s->flags |= SN_CURR_SESS;
 		s->srv->cur_sess++;
diff --git a/src/client.c b/src/client.c
index 45c576d..3e2099e 100644
--- a/src/client.c
+++ b/src/client.c
@@ -190,7 +190,7 @@
 		s->si[0].chk_rcv = stream_sock_chk_rcv;
 		s->si[0].chk_snd = stream_sock_chk_snd;
 		s->si[0].fd = cfd;
-		s->si[0].flags = SI_FL_NONE;
+		s->si[0].flags = SI_FL_NONE | SI_FL_CAP_SPLTCP; /* TCP splicing capable */
 		s->si[0].exp = TICK_ETERNITY;
 
 		s->si[1].state = s->si[1].prev_state = SI_ST_INI;
diff --git a/src/session.c b/src/session.c
index 9ea45e1..d3f3710 100644
--- a/src/session.c
+++ b/src/session.c
@@ -184,6 +184,7 @@
 	if (unlikely(si->flags & (SI_FL_EXP|SI_FL_ERR))) {
 		si->exp   = TICK_ETERNITY;
 		si->state = SI_ST_CER;
+		si->flags &= ~SI_FL_CAP_SPLICE;
 		fd_delete(si->fd);
 
 		if (si->err_type)
@@ -207,6 +208,7 @@
 		si->shutw(si);
 		si->err_type |= SI_ET_CONN_ABRT;
 		si->err_loc  = s->srv;
+		si->flags &= ~SI_FL_CAP_SPLICE;
 		if (s->srv_error)
 			s->srv_error(s, si);
 		return 1;
@@ -859,6 +861,7 @@
 	if (!(s->req->flags & (BF_KERN_SPLICING|BF_SHUTR)) &&
 	    s->req->to_forward &&
 	    (global.tune.options & GTUNE_USE_SPLICE) &&
+	    (s->si[0].flags & s->si[1].flags & SI_FL_CAP_SPLICE) &&
 	    (pipes_used < global.maxpipes) &&
 	    (((s->fe->options2|s->be->options2) & PR_O2_SPLIC_REQ) ||
 	     (((s->fe->options2|s->be->options2) & PR_O2_SPLIC_AUT) &&
@@ -966,6 +969,7 @@
 	if (!(s->rep->flags & (BF_KERN_SPLICING|BF_SHUTR)) &&
 	    s->rep->to_forward &&
 	    (global.tune.options & GTUNE_USE_SPLICE) &&
+	    (s->si[0].flags & s->si[1].flags & SI_FL_CAP_SPLICE) &&
 	    (pipes_used < global.maxpipes) &&
 	    (((s->fe->options2|s->be->options2) & PR_O2_SPLIC_RTR) ||
 	     (((s->fe->options2|s->be->options2) & PR_O2_SPLIC_AUT) &&
diff --git a/src/stream_sock.c b/src/stream_sock.c
index 52305c5..438ff0a 100644
--- a/src/stream_sock.c
+++ b/src/stream_sock.c
@@ -186,6 +186,16 @@
 				retval = -1;
 				break;
 			}
+
+			if (errno == ENOSYS) {
+				/* splice not supported on this end, disable it */
+				b->flags &= ~BF_KERN_SPLICING;
+				si->flags &= ~SI_FL_CAP_SPLICE;
+				put_pipe(b->pipe);
+				b->pipe = NULL;
+				return -1;
+			}
+
 			/* here we have another error */
 			si->flags |= SI_FL_ERR;
 			retval = 1;