MEDIUM: session: add a pointer to a struct task in the session

The session may need to enforce a timeout when waiting for a handshake.
Till now we used a trick to avoid allocating a pointer, we used to set
the connection's owner to the task and set the task's context to the
session, so that it was possible to circle between all of them. The
problem is that we'll really need to pass the pointer to the session
to the upper layers during initialization and that the only place to
store it is conn->owner, which is squatted for this trick.

So this patch moves the struct task* into the session where it should
always have been and ensures conn->owner points to the session until
the data layer is properly initialized.
This commit is contained in:
Willy Tarreau 2017-08-28 19:02:51 +02:00
parent ca3610251b
commit 0b74eae1f1
3 changed files with 33 additions and 26 deletions

View File

@ -299,7 +299,7 @@ struct connection {
const struct xprt_ops *xprt; /* operations at the transport layer */
const struct data_cb *data; /* data layer callbacks. Must be set before xprt->init() */
void *xprt_ctx; /* general purpose pointer, initialized to NULL */
void *owner; /* pointer to upper layer's entity (eg: stream interface) */
void *owner; /* pointer to upper layer's entity (eg: session, stream interface) */
int xprt_st; /* transport layer state, initialized to zero */
union conn_handle handle; /* connection handle at the socket layer */
enum obj_type *target; /* the target to connect to (server, proxy, applet, ...) */

View File

@ -46,6 +46,7 @@ struct session {
struct timeval tv_accept; /* date of the session's accept() in internal date (monotonic) */
struct stkctr stkctr[MAX_SESS_STKCTR]; /* stick counters for tcp-connection */
struct vars vars; /* list of variables for the session scope. */
struct task *task; /* handshake timeout processing */
};
#endif /* _TYPES_SESSION_H */

View File

@ -52,6 +52,7 @@ struct session *session_new(struct proxy *fe, struct listener *li, enum obj_type
sess->tv_accept = now; /* corrected date for internal use */
memset(sess->stkctr, 0, sizeof(sess->stkctr));
vars_init(&sess->vars, SCOPE_SESS);
sess->task = NULL;
}
return sess;
}
@ -102,7 +103,8 @@ static void session_count_new(struct session *sess)
* returns a positive value upon success, 0 if the connection can be ignored,
* or a negative value upon critical failure. The accepted file descriptor is
* closed if we return <= 0. If no handshake is needed, it immediately tries
* to instanciate a new stream.
* to instanciate a new stream. The created connection's owner points to the
* new session until the upper layers are created.
*/
int session_accept_fd(struct listener *l, int cfd, struct sockaddr_storage *addr)
{
@ -148,6 +150,8 @@ int session_accept_fd(struct listener *l, int cfd, struct sockaddr_storage *addr
if (!sess)
goto out_free_conn;
conn_set_owner(cli_conn, sess);
p->feconn++;
/* This session was accepted, count it now */
if (p->feconn > p->fe_counters.conn_max)
@ -222,32 +226,33 @@ int session_accept_fd(struct listener *l, int cfd, struct sockaddr_storage *addr
if (global.tune.client_rcvbuf)
setsockopt(cfd, SOL_SOCKET, SO_RCVBUF, &global.tune.client_rcvbuf, sizeof(global.tune.client_rcvbuf));
/* OK, now either we have a pending handshake to execute with and
* then we must return to the I/O layer, or we can proceed with the
* end of the stream initialization. In case of handshake, we also
* set the I/O timeout to the frontend's client timeout.
/* OK, now either we have a pending handshake to execute with and then
* we must return to the I/O layer, or we can proceed with the end of
* the stream initialization. In case of handshake, we also set the I/O
* timeout to the frontend's client timeout and register a task in the
* session for this purpose. The connection's owner is left to the
* session during this period.
*
* At this point we set the relation between sess/task/conn this way :
*
* orig -- sess <-- context
* | |
* v |
* conn -- owner ---> task
* +----------------- task
* | |
* orig -- sess <-- context |
* | ^ | |
* v | | |
* conn -- owner ---> task <-----+
*/
if (cli_conn->flags & CO_FL_HANDSHAKE) {
struct task *t;
if (unlikely((t = task_new()) == NULL))
if (unlikely((sess->task = task_new()) == NULL))
goto out_free_sess;
conn_set_owner(cli_conn, t);
conn_set_xprt_done_cb(cli_conn, conn_complete_session);
t->context = sess;
t->nice = l->nice;
t->process = session_expire_embryonic;
t->expire = tick_add_ifset(now_ms, p->timeout.client);
task_queue(t);
sess->task->context = sess;
sess->task->nice = l->nice;
sess->task->process = session_expire_embryonic;
sess->task->expire = tick_add_ifset(now_ms, p->timeout.client);
task_queue(sess->task);
return 1;
}
@ -327,10 +332,11 @@ static void session_prepare_log_prefix(struct session *sess)
* disabled and finally kills the file descriptor. This function requires that
* sess->origin points to the incoming connection.
*/
static void session_kill_embryonic(struct session *sess, struct task *task)
static void session_kill_embryonic(struct session *sess)
{
int level = LOG_INFO;
struct connection *conn = __objt_conn(sess->origin);
struct task *task = sess->task;
unsigned int log = sess->fe->to_log;
const char *err_msg;
@ -401,7 +407,7 @@ static struct task *session_expire_embryonic(struct task *t)
if (!(t->state & TASK_WOKEN_TIMER))
return t;
session_kill_embryonic(sess, t);
session_kill_embryonic(sess);
return NULL;
}
@ -410,8 +416,7 @@ static struct task *session_expire_embryonic(struct task *t)
*/
static int conn_complete_session(struct connection *conn)
{
struct task *task = conn->owner;
struct session *sess = task->context;
struct session *sess = conn->owner;
struct stream *strm;
conn_clear_xprt_done_cb(conn);
@ -437,12 +442,13 @@ static int conn_complete_session(struct connection *conn)
task_wakeup(strm->task, TASK_WOKEN_INIT);
/* the embryonic session's task is not needed anymore */
task_delete(task);
task_free(task);
task_delete(sess->task);
task_free(sess->task);
sess->task = NULL;
return 0;
fail:
session_kill_embryonic(sess, task);
session_kill_embryonic(sess);
return -1;
}