]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: buffer: implement b_alloc_margin()
authorWilly Tarreau <w@1wt.eu>
Tue, 2 Dec 2014 12:54:01 +0000 (13:54 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 24 Dec 2014 22:47:32 +0000 (23:47 +0100)
This function is used to allocate a buffer and ensure that we leave
some margin after it in the pool. The function is not obvious. While
we allocate only one buffer, we want to ensure that at least two remain
available after our allocation. The purpose is to ensure we'll never
enter a deadlock where all sessions allocate exactly one buffer, and
none of them will be able to allocate the second buffer needed to build
a response in order to release the first one.

We also take care of remaining fast in the the fast path by first
checking whether or not there is enough margin, in which case we only
rely on b_alloc_fast() which is guaranteed to succeed. Otherwise we
take the slow path using pool_refill_alloc().

include/common/buffer.h

index b1c16e8a55d8318e28ada4b6bf92d8f396c75fcc..cea31aa5f538269e2994c1b3a4a7231617c0b30b 100644 (file)
@@ -464,6 +464,34 @@ static inline void b_free(struct buffer **buf)
        *buf = &buf_empty;
 }
 
+/* Ensures that <buf> is allocated. If an allocation is needed, it ensures that
+ * there are still at least <margin> buffers available in the pool after this
+ * allocation so that we don't leave the pool in a condition where a session or
+ * a response buffer could not be allocated anymore, resulting in a deadlock.
+ * This means that we sometimes need to try to allocate extra entries even if
+ * only one buffer is needed.
+ */
+static inline struct buffer *b_alloc_margin(struct buffer **buf, int margin)
+{
+       struct buffer *next;
+
+       if ((*buf)->size)
+               return *buf;
+
+       /* fast path */
+       if ((pool2_buffer->allocated - pool2_buffer->used) > margin)
+               return b_alloc_fast(buf);
+
+       next = pool_refill_alloc(pool2_buffer, margin);
+       if (!next)
+               return next;
+
+       next->size = pool2_buffer->size - sizeof(struct buffer);
+       b_reset(next);
+       *buf = next;
+       return next;
+}
+
 #endif /* _COMMON_BUFFER_H */
 
 /*