static void
wsl(struct worker *wrk, enum VSL_tag_e tag, int id, const char *fmt, va_list ap)
{
	char *p;
	unsigned n, mlen;
	txt t;

	AN(fmt);
	mlen = cache_param->shm_reclen;

	if (strchr(fmt, '%') == NULL) {
		t.b = TRUST_ME(fmt);
		t.e = strchr(t.b, '\0');
		WSLR(wrk, tag, id, t);
	} else {
		assert(wrk->wlp < wrk->wle);

		/* Wrap if we cannot fit a full size record */
		if (VSL_END(wrk->wlp, mlen) >= wrk->wle)
			WSL_Flush(wrk, 1);

		p = VSL_DATA(wrk->wlp);
		n = vsnprintf(p, mlen, fmt, ap);
		if (n > mlen)
			n = mlen;	/* we truncate long fields */
		vsl_hdr(tag, wrk->wlp, n, id);
		wrk->wlp = VSL_END(wrk->wlp, n);
		assert(wrk->wlp < wrk->wle);
		wrk->wlr++;
	}
	if (cache_param->diag_bitmap & 0x10000)
		WSL_Flush(wrk, 0);
}
void
WSLR(struct worker *wrk, enum VSL_tag_e tag, int id, txt t)
{
	unsigned l, mlen;

	Tcheck(t);
	mlen = cache_param->shm_reclen;

	/* Truncate */
	l = Tlen(t);
	if (l > mlen) {
		l = mlen;
		t.e = t.b + l;
	}

	assert(wrk->wlp < wrk->wle);

	/* Wrap if necessary */
	if (VSL_END(wrk->wlp, l) >= wrk->wle)
		WSL_Flush(wrk, 1);
	assert (VSL_END(wrk->wlp, l) < wrk->wle);
	memcpy(VSL_DATA(wrk->wlp), t.b, l);
	vsl_hdr(tag, wrk->wlp, l, id);
	wrk->wlp = VSL_END(wrk->wlp, l);
	assert(wrk->wlp < wrk->wle);
	wrk->wlr++;
	if (cache_param->diag_bitmap & 0x10000)
		WSL_Flush(wrk, 0);
}
void
VDI_RecycleFd(struct worker *wrk, struct vbc **vbp)
{
	struct backend *bp;
	struct vbc *vc;

	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
	AN(vbp);
	vc = *vbp;
	*vbp = NULL;
	CHECK_OBJ_NOTNULL(vc, VBC_MAGIC);
	CHECK_OBJ_NOTNULL(vc->backend, BACKEND_MAGIC);
	assert(vc->fd >= 0);

	bp = vc->backend;

	WSL(wrk, SLT_BackendReuse, vc->vsl_id, "%s", bp->display_name);
	/*
	 * Flush the shmlog, so that another session reusing this backend
	 * will log chronologically later than our use of it.
	 */
	WSL_Flush(wrk, 0);
	Lck_Lock(&bp->mtx);
	VSC_C_main->backend_recycle++;
	VTAILQ_INSERT_HEAD(&bp->connlist, vc, list);
	VBE_DropRefLocked(bp);
}
void
VDI_CloseFd(struct worker *wrk, struct vbc **vbp)
{
	struct backend *bp;
	struct vbc *vc;

	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
	AN(vbp);
	vc = *vbp;
	*vbp = NULL;
	CHECK_OBJ_NOTNULL(vc, VBC_MAGIC);
	CHECK_OBJ_NOTNULL(vc->backend, BACKEND_MAGIC);
	assert(vc->fd >= 0);

	bp = vc->backend;

	WSL(wrk, SLT_BackendClose, vc->vsl_id, "%s", bp->display_name);

	/* Checkpoint log to flush all info related to this connection
	   before the OS reuses the FD */
	WSL_Flush(wrk, 0);

	VTCP_close(&vc->fd);
	VBE_DropRefConn(bp);
	vc->backend = NULL;
	VBE_ReleaseConn(vc);
}
void
CNT_Session(struct sess *sp)
{
    int done;
    struct worker *w;

    CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
    w = sp->wrk;
    CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);

    /*
     * Possible entrance states
     */
    assert(
        sp->step == STP_FIRST ||
        sp->step == STP_START ||
        sp->step == STP_LOOKUP ||
        sp->step == STP_RECV);

    /*
     * Whenever we come in from the acceptor we need to set blocking
     * mode, but there is no point in setting it when we come from
     * ESI or when a parked sessions returns.
     * It would be simpler to do this in the acceptor, but we'd rather
     * do the syscall in the worker thread.
     */
    if (sp->step == STP_FIRST || sp->step == STP_START)
        TCP_blocking(sp->fd);

    for (done = 0; !done; ) {
        assert(sp->wrk == w);
        /*
         * This is a good place to be paranoid about the various
         * pointers still pointing to the things we expect.
         */
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
        CHECK_OBJ_ORNULL(sp->obj, OBJECT_MAGIC);
        CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
        CHECK_OBJ_ORNULL(w->nobjhead, OBJHEAD_MAGIC);
        WS_Assert(w->ws);

        switch (sp->step) {
#define STEP(l,u) \
		    case STP_##u: \
			if (params->diag_bitmap & 0x01) \
				cnt_diag(sp, #u); \
			done = cnt_##l(sp); \
		        break;
#include "steps.h"
#undef STEP
        default:
            WRONG("State engine misfire");
        }
        WS_Assert(w->ws);
        CHECK_OBJ_ORNULL(w->nobjhead, OBJHEAD_MAGIC);
    }
    WSL_Flush(w, 0);
    AZ(w->wfd);
}
static void *
exp_timer(struct sess *sp, void *priv)
{
	struct objcore *oc;
	struct object *o;
	double t;
	struct objcore_head *lru;

	(void)priv;
	AZ(sleep(10));		/* XXX: Takes time for VCL to arrive */
	VCL_Get(&sp->vcl);
	t = TIM_real();
	while (1) {
		Lck_Lock(&exp_mtx);
		oc = binheap_root(exp_heap);
		CHECK_OBJ_ORNULL(oc, OBJCORE_MAGIC);
		if (oc == NULL || oc->timer_when > t) { /* XXX: > or >= ? */
			Lck_Unlock(&exp_mtx);
			WSL_Flush(sp->wrk, 0);
			WRK_SumStat(sp->wrk);
			AZ(sleep(1));
			VCL_Refresh(&sp->vcl);
			t = TIM_real();
			continue;
		}

		o = oc->obj;
		CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
		CHECK_OBJ_NOTNULL(o->objhead, OBJHEAD_MAGIC);
		assert(oc->flags & OC_F_ONLRU);
		assert(oc->timer_idx != BINHEAP_NOIDX);
		binheap_delete(exp_heap, oc->timer_idx);
		assert(oc->timer_idx == BINHEAP_NOIDX);
		lru = STV_lru(o->objstore);
		AN(lru);
		VTAILQ_REMOVE(lru, o->objcore, lru_list);
		oc->flags &= ~OC_F_ONLRU;

		{	/* Sanity checking */
			struct objcore *oc2 = binheap_root(exp_heap);
			if (oc2 != NULL) {
				assert(oc2->timer_idx != BINHEAP_NOIDX);
				assert(oc2->timer_when >= oc->timer_when);
			}
		}

		VSL_stats->n_expired++;
		Lck_Unlock(&exp_mtx);
		WSL(sp->wrk, SLT_ExpKill, 0, "%u %d",
		    o->xid, (int)(o->ttl - t));
		HSH_Deref(sp->wrk, &o);
	}
}
Beispiel #7
0
static void
cnt_diag(struct sess *sp, const char *state)
{
    if (sp->wrk != NULL) {
        WSP(sp, SLT_Debug, "thr %p STP_%s sp %p obj %p vcl %p",
            pthread_self(), state, sp, sp->wrk->obj, sp->vcl);
        WSL_Flush(sp->wrk, 0);
    } else {
        VSL(SLT_Debug, sp->vsl_id,
            "thr %p STP_%s sp %p obj %p vcl %p",
            pthread_self(), state, sp, sp->wrk->obj, sp->vcl);
    }
}
Beispiel #8
0
void
SES_ReleaseReq(struct sess *sp)
{
	struct sesspool *pp;

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	pp = sp->sesspool;
	CHECK_OBJ_NOTNULL(pp, SESSPOOL_MAGIC);
	AN(pp->pool);
	CHECK_OBJ_NOTNULL(sp->req, REQ_MAGIC);
	MPL_AssertSane(sp->req);
	WSL_Flush(sp->req->vsl, 0);
	MPL_Free(pp->mpl_req, sp->req);
	sp->req = NULL;
}
Beispiel #9
0
enum pipe_status
PIE_Session(struct pipe *dp)
{
	enum pipe_status status = PIPE_CONTINUE;
	struct worker *w;

	CHECK_OBJ_NOTNULL(dp, PIPE_MAGIC);
	CAST_OBJ_NOTNULL(w, dp->wrk, WORKER_MAGIC);

	for (;status == PIPE_CONTINUE;) {
		assert(dp->wrk == w);

		if (params->diag_bitmap & 0x00080000)
			WSL(dp->wrk, SLT_PipeSM, dp->vc.vc_fd, "%s",
			    pie_stepstr[dp->step]);

#ifdef VARNISH_DEBUG
		dp->stephist[dp->stephist_cur++] = dp->step;
		if (dp->stephist_cur >= STEPHIST_MAX)
			dp->stephist_cur = 0;
#endif
		switch (dp->step) {
#define PIPESTEP(l,u)				\
		case PIE_##u:			\
			status = pie_##l(dp);	\
			break;
#include "pipesteps.h"
#undef PIPESTEP
		default:
			WRONG("Drainpipe state engine misfire");
		}
		assert(dp->wrk == w);

		if (params->diag_bitmap & 0x00080000)
			WSL(dp->wrk, SLT_PipeSM, dp->vc.vc_fd, "%s",
			    pie_statusstr[status]);
	}
	WSL_Flush(w, 0);
	return (status);
}
static int
cnt_done(struct sess *sp)
{
    double dh, dp, da;
    int i;

    CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
    CHECK_OBJ_ORNULL(sp->vcl, VCL_CONF_MAGIC);

    AZ(sp->obj);
    AZ(sp->vbe);
    sp->director = NULL;
    sp->restarts = 0;

    if (sp->vcl != NULL && sp->esis == 0) {
        if (sp->wrk->vcl != NULL)
            VCL_Rel(&sp->wrk->vcl);
        sp->wrk->vcl = sp->vcl;
        sp->vcl = NULL;
    }

    sp->t_end = TIM_real();
    sp->wrk->lastused = sp->t_end;
    if (sp->xid == 0) {
        sp->t_req = sp->t_end;
        sp->t_resp = sp->t_end;
    }
    dp = sp->t_resp - sp->t_req;
    da = sp->t_end - sp->t_resp;
    dh = sp->t_req - sp->t_open;
    WSL(sp->wrk, SLT_ReqEnd, sp->id, "%u %.9f %.9f %.9f %.9f %.9f",
        sp->xid, sp->t_req, sp->t_end, dh, dp, da);

    sp->xid = 0;
    sp->t_open = sp->t_end;
    sp->t_resp = NAN;
    WSL_Flush(sp->wrk, 0);

    /* If we did an ESI include, don't mess up our state */
    if (sp->esis > 0)
        return (1);

    sp->t_req = NAN;

    if (sp->fd >= 0 && sp->doclose != NULL) {
        /*
         * This is an orderly close of the connection; ditch nolinger
         * before we close, to get queued data transmitted.
         */
        TCP_linger(sp->fd, 0);
        vca_close_session(sp, sp->doclose);
    }
    if (sp->fd < 0) {
        SES_Charge(sp);
        VSL_stats->sess_closed++;
        sp->wrk = NULL;
        SES_Delete(sp);
        return (1);
    }

    /* Reset the workspace to the session-watermark */
    WS_Reset(sp->ws, sp->ws_ses);

    i = HTC_Reinit(sp->htc);
    if (i == 1) {
        VSL_stats->sess_pipeline++;
        sp->step = STP_START;
        return (0);
    }
    if (Tlen(sp->htc->rxbuf)) {
        VSL_stats->sess_readahead++;
        sp->step = STP_WAIT;
        return (0);
    }
    if (params->session_linger > 0) {
        VSL_stats->sess_linger++;
        sp->step = STP_WAIT;
        return (0);
    }
    VSL_stats->sess_herd++;
    SES_Charge(sp);
    sp->wrk = NULL;
    vca_return_session(sp);
    return (1);
}
int
FetchHdr(struct sess *sp, int need_host_hdr)
{
	struct vbc *vc;
	struct worker *wrk;
	struct http *hp;
	int retry = -1;
	int i;
	struct http_conn *htc;

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
	wrk = sp->wrk;
	CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC);
	htc = &wrk->busyobj->htc;

	AN(sp->req->director);
	AZ(sp->req->obj);

	if (sp->req->objcore != NULL) {		/* pass has no objcore */
		CHECK_OBJ_NOTNULL(sp->req->objcore, OBJCORE_MAGIC);
		AN(sp->req->objcore->flags & OC_F_BUSY);
	}

	hp = wrk->busyobj->bereq;

	sp->wrk->busyobj->vbc = VDI_GetFd(NULL, sp);
	if (sp->wrk->busyobj->vbc == NULL) {
		WSP(sp, SLT_FetchError, "no backend connection");
		return (-1);
	}
	vc = sp->wrk->busyobj->vbc;
	if (vc->recycled)
		retry = 1;

	/*
	 * Now that we know our backend, we can set a default Host:
	 * header if one is necessary.  This cannot be done in the VCL
	 * because the backend may be chosen by a director.
	 */
	if (need_host_hdr)
		VDI_AddHostHeader(sp->wrk, vc);

	(void)VTCP_blocking(vc->fd);	/* XXX: we should timeout instead */
	WRW_Reserve(wrk, &vc->fd);
	(void)http_Write(wrk, vc->vsl_id, hp, 0);	/* XXX: stats ? */

	/* Deal with any message-body the request might have */
	i = FetchReqBody(sp);
	if (WRW_FlushRelease(wrk) || i > 0) {
		WSP(sp, SLT_FetchError, "backend write error: %d (%s)",
		    errno, strerror(errno));
		VDI_CloseFd(sp->wrk, &sp->wrk->busyobj->vbc);
		/* XXX: other cleanup ? */
		return (retry);
	}

	/* Checkpoint the vsl.here */
	WSL_Flush(wrk, 0);

	/* XXX is this the right place? */
	VSC_C_main->backend_req++;

	/* Receive response */

	HTC_Init(htc, wrk->ws, vc->fd, vc->vsl_id,
	    cache_param->http_resp_size,
	    cache_param->http_resp_hdr_len);

	VTCP_set_read_timeout(vc->fd, vc->first_byte_timeout);

	i = HTC_Rx(htc);

	if (i < 0) {
		WSP(sp, SLT_FetchError, "http first read error: %d %d (%s)",
		    i, errno, strerror(errno));
		VDI_CloseFd(sp->wrk, &sp->wrk->busyobj->vbc);
		/* XXX: other cleanup ? */
		/* Retryable if we never received anything */
		return (i == -1 ? retry : -1);
	}

	VTCP_set_read_timeout(vc->fd, vc->between_bytes_timeout);

	while (i == 0) {
		i = HTC_Rx(htc);
		if (i < 0) {
			WSP(sp, SLT_FetchError,
			    "http first read error: %d %d (%s)",
			    i, errno, strerror(errno));
			VDI_CloseFd(sp->wrk, &sp->wrk->busyobj->vbc);
			/* XXX: other cleanup ? */
			return (-1);
		}
	}

	hp = wrk->busyobj->beresp;

	if (http_DissectResponse(wrk, htc, hp)) {
		WSP(sp, SLT_FetchError, "http format error");
		VDI_CloseFd(sp->wrk, &sp->wrk->busyobj->vbc);
		/* XXX: other cleanup ? */
		return (-1);
	}
	return (0);
}
Beispiel #12
0
static int
cnt_done(struct sess *sp)
{
    double dh, dp, da;
    int i;
    struct worker *wrk;

    CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
    wrk = sp->wrk;
    CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
    CHECK_OBJ_ORNULL(sp->vcl, VCL_CONF_MAGIC);

    AZ(wrk->obj);
    AZ(wrk->busyobj);
    sp->director = NULL;
    sp->restarts = 0;

    wrk->busyobj = NULL;

    SES_Charge(sp);

    /* If we did an ESI include, don't mess up our state */
    if (sp->esi_level > 0)
        return (1);

    if (sp->vcl != NULL) {
        if (wrk->vcl != NULL)
            VCL_Rel(&wrk->vcl);
        wrk->vcl = sp->vcl;
        sp->vcl = NULL;
    }


    sp->t_end = W_TIM_real(wrk);
    WSP(sp, SLT_Debug, "PHK req %.9f resp %.9f end %.9f open %.9f",
        sp->t_req, sp->t_resp, sp->t_end,  sp->t_open);
    if (sp->xid == 0) {
        // sp->t_req = sp->t_end;
        sp->t_resp = sp->t_end;
    } else {
        dp = sp->t_resp - sp->t_req;
        da = sp->t_end - sp->t_resp;
        dh = sp->t_req - sp->t_open;
        /* XXX: Add StatReq == StatSess */
        /* XXX: Workaround for pipe */
        if (sp->fd >= 0) {
            WSP(sp, SLT_Length, "%ju",
                (uintmax_t)sp->req_bodybytes);
        }
        WSP(sp, SLT_ReqEnd, "%u %.9f %.9f %.9f %.9f %.9f",
            sp->xid, sp->t_req, sp->t_end, dh, dp, da);
    }
    sp->xid = 0;
    WSL_Flush(wrk, 0);

    sp->t_open = sp->t_end;
    sp->t_resp = NAN;

    sp->req_bodybytes = 0;

    sp->t_req = NAN;
    sp->hash_always_miss = 0;
    sp->hash_ignore_busy = 0;

    if (sp->fd >= 0 && sp->doclose != NULL) {
        /*
         * This is an orderly close of the connection; ditch nolinger
         * before we close, to get queued data transmitted.
         */
        // XXX: not yet (void)VTCP_linger(sp->fd, 0);
        SES_Close(sp, sp->doclose);
    }

    if (sp->fd < 0) {
        wrk->stats.sess_closed++;
        SES_Delete(sp, NULL);
        return (1);
    }

    if (wrk->stats.client_req >= cache_param->wthread_stats_rate)
        WRK_SumStat(wrk);
    /* Reset the workspace to the session-watermark */
    WS_Reset(sp->ws, sp->ws_ses);
    WS_Reset(wrk->ws, NULL);

    i = HTC_Reinit(sp->htc);
    if (i == 1) {
        wrk->stats.sess_pipeline++;
        sp->step = STP_START;
        return (0);
    }
    if (Tlen(sp->htc->rxbuf)) {
        wrk->stats.sess_readahead++;
        sp->step = STP_WAIT;
        return (0);
    }
    if (cache_param->session_linger > 0) {
        wrk->stats.sess_linger++;
        sp->step = STP_WAIT;
        return (0);
    }
    wrk->stats.sess_herd++;
    Pool_Wait(sp);
    return (1);
}
Beispiel #13
0
void
CNT_Session(struct sess *sp)
{
    int done;
    struct worker *wrk;

    CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
    wrk = sp->wrk;
    CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);

    /*
     * Possible entrance states
     */
    assert(
        sp->step == STP_FIRST ||
        sp->step == STP_START ||
        sp->step == STP_LOOKUP ||
        sp->step == STP_RECV);

    AZ(wrk->obj);
    AZ(wrk->objcore);

    /*
     * Whenever we come in from the acceptor or waiter, we need to set
     * blocking mode, but there is no point in setting it when we come from
     * ESI or when a parked sessions returns.
     * It would be simpler to do this in the acceptor or waiter, but we'd
     * rather do the syscall in the worker thread.
     * On systems which return errors for ioctl, we close early
     */
    if ((sp->step == STP_FIRST || sp->step == STP_START) &&
            VTCP_blocking(sp->fd)) {
        if (errno == ECONNRESET)
            SES_Close(sp, "remote closed");
        else
            SES_Close(sp, "error");
        sp->step = STP_DONE;
    }

    /*
     * NB: Once done is set, we can no longer touch sp!
     */
    for (done = 0; !done; ) {
        assert(sp->wrk == wrk);
        /*
         * This is a good place to be paranoid about the various
         * pointers still pointing to the things we expect.
         */
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
        CHECK_OBJ_ORNULL(wrk->obj, OBJECT_MAGIC);
        CHECK_OBJ_ORNULL(wrk->nobjhead, OBJHEAD_MAGIC);
        WS_Assert(wrk->ws);

        switch (sp->step) {
#define STEP(l,u) \
		    case STP_##u: \
			if (cache_param->diag_bitmap & 0x01) \
				cnt_diag(sp, #u); \
			done = cnt_##l(sp); \
		        break;
#include "tbl/steps.h"
#undef STEP
        default:
            WRONG("State engine misfire");
        }
        WS_Assert(wrk->ws);
        CHECK_OBJ_ORNULL(wrk->nobjhead, OBJHEAD_MAGIC);
    }
    WSL_Flush(wrk, 0);
    AZ(wrk->obj);
    AZ(wrk->objcore);
#define ACCT(foo)	AZ(wrk->acct_tmp.foo);
#include "tbl/acct_fields.h"
#undef ACCT
    assert(WRW_IsReleased(wrk));
}