From 6a50be48750c6f8f6d3b953b36b0c18947cf62b7 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 24 Nov 2014 11:55:08 +0100 Subject: MEDIUM: buffers: add a new buf_wanted dummy buffer to report failed allocations Doing so ensures that even when no memory is available, we leave the channel in a sane condition. There's a special case in proto_http.c regarding the compression, we simply pre-allocate the tmpbuf to point to the dummy buffer. Not reusing &buf_empty for this allows the rest of the code to differenciate an empty buffer that's not used from an empty buffer that results from a failed allocation which has the same semantics as a buffer full. --- include/common/buffer.h | 22 ++++++++++++++-------- src/buffer.c | 8 ++++++-- src/proto_http.c | 4 ++-- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/include/common/buffer.h b/include/common/buffer.h index e667574..30ae363 100644 --- a/include/common/buffer.h +++ b/include/common/buffer.h @@ -41,6 +41,7 @@ struct buffer { extern struct pool_head *pool2_buffer; extern struct buffer buf_empty; +extern struct buffer buf_wanted; int init_buffer(); int buffer_replace2(struct buffer *b, char *pos, char *end, const char *str, int len); @@ -396,18 +397,23 @@ static inline void b_reset(struct buffer *buf) buf->p = buf->data; } -/* Allocates a buffer and replaces *buf with this buffer. No control is made - * to check if *buf already pointed to another buffer. The allocated buffer - * is returned, or NULL in case no memory is available. +/* Allocates a buffer and replaces *buf with this buffer. If no memory is + * available, &buf_wanted is used instead. No control is made to check if *buf + * already pointed to another buffer. The allocated buffer is returned, or + * NULL in case no memory is available. */ static inline struct buffer *b_alloc(struct buffer **buf) { - *buf = pool_alloc2(pool2_buffer); - if (likely(*buf)) { - (*buf)->size = pool2_buffer->size - sizeof(struct buffer); - b_reset(*buf); + struct buffer *b; + + *buf = &buf_wanted; + b = pool_alloc2(pool2_buffer); + if (likely(b)) { + b->size = pool2_buffer->size - sizeof(struct buffer); + b_reset(b); + *buf = b; } - return *buf; + return b; } /* Releases buffer *buf (no check of emptiness) */ diff --git a/src/buffer.c b/src/buffer.c index 7691026..d9301bf 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -22,10 +22,14 @@ struct pool_head *pool2_buffer; -/* this buffer is used to have a valid pointer to an empty buffer in channels - * which convey no more data. +/* These buffers are used to always have a valid pointer to an empty buffer in + * channels. The first buffer is set once a buffer is empty. The second one is + * set when a buffer is desired but no more are available. It helps knowing + * what channel wants a buffer. They can reliably be exchanged, the split + * between the two is only an optimization. */ struct buffer buf_empty = { .p = buf_empty.data }; +struct buffer buf_wanted = { .p = buf_wanted.data }; /* perform minimal intializations, report 0 in case of error, 1 if OK. */ int init_buffer() diff --git a/src/proto_http.c b/src/proto_http.c index 4f6f70a..f5c7c4c 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -6516,7 +6516,7 @@ int http_response_forward_body(struct session *s, struct channel *res, int an_bi { struct http_txn *txn = &s->txn; struct http_msg *msg = &s->txn.rsp; - static struct buffer *tmpbuf = NULL; + static struct buffer *tmpbuf = &buf_empty; int compressing = 0; int ret; @@ -6570,7 +6570,7 @@ int http_response_forward_body(struct session *s, struct channel *res, int an_bi * output of compressed data, and in CRLF state to let the * TRAILERS state finish the job of removing the trailing CRLF. */ - if (unlikely(tmpbuf == NULL)) { + if (unlikely(!tmpbuf->size)) { /* this is the first time we need the compression buffer */ if (b_alloc(&tmpbuf) == NULL) goto aborted_xfer; /* no memory */ -- 1.7.12.1