]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] http: don't use trash to realign large buffers
authorWilly Tarreau <w@1wt.eu>
Thu, 25 Feb 2010 22:54:31 +0000 (23:54 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 25 Feb 2010 22:54:31 +0000 (23:54 +0100)
The trash buffer may now be smaller than a buffer because we can tune
it at run time. This causes a risk when we're trying to use it as a
temporary buffer to realign unaligned requests, because we may have to
put up to a full buffer into it.

Instead of doing a double copy, we're now relying on an open-coded
bouncing copy algorithm. The principle is that we move one byte at
a time to its final place, and if that place also holds a byte, then
we move it too, and so on. We finish when we've moved all the buffer.
It limits the number of memory accesses, but since it proceeds one
byte at a time and with random walk, it's not cache friendly and
should be slower than a double copy. However, it's only used in
extreme situations and the difference will not be noticeable.

It has been extensively tested and works reliably.

include/proto/buffers.h
src/buffers.c
src/proto_http.c

index e046f39276c0ffb6c02e8b3076f1f903f482bc41..f174b8770c489c5ab0fec911c1577dda1320161c 100644 (file)
@@ -2,7 +2,7 @@
  * include/proto/buffers.h
  * Buffer management definitions, macros and inline functions.
  *
- * Copyright (C) 2000-2009 Willy Tarreau - w@1wt.eu
+ * Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -429,6 +429,8 @@ int buffer_replace(struct buffer *b, char *pos, char *end, const char *str);
 int buffer_replace2(struct buffer *b, char *pos, char *end, const char *str, int len);
 int buffer_insert_line2(struct buffer *b, char *pos, const char *str, int len);
 void buffer_dump(FILE *o, struct buffer *b, int from, int to);
+void buffer_bounce_realign(struct buffer *buf);
+
 
 
 /* writes the chunk <chunk> to buffer <buf>. Returns -1 in case of success,
index 5f3cc2c440e27b910373d273e584a929cd0da639..79cc45bf581d02d940382e3a2e4994da755dddd9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Buffer management functions.
  *
- * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
+ * Copyright 2000-2010 Willy Tarreau <w@1wt.eu>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -298,6 +298,68 @@ int buffer_insert_line2(struct buffer *b, char *pos, const char *str, int len)
 }
 
 
+/* Realigns a possibly non-contiguous buffer by bouncing bytes from source to
+ * destination. It does not use any intermediate buffer and does the move in
+ * place, though it will be slower than a simple memmove() on contiguous data,
+ * so it's desirable to use it only on non-contiguous buffers. No pointers are
+ * changed, the caller is responsible for that.
+ */
+void buffer_bounce_realign(struct buffer *buf)
+{
+       int advance, to_move;
+       char *from, *to;
+
+       advance = buf->data + buf->size - buf->w;
+       if (!advance)
+               return;
+
+       from = buf->w;
+       to_move = buf->l;
+       while (to_move) {
+               char last, save;
+
+               last = *from;
+               to = from + advance;
+               if (to >= buf->data + buf->size)
+                       to -= buf->size;
+
+               while (1) {
+                       save = *to;
+                       *to  = last;
+                       last = save;
+                       to_move--;
+                       if (!to_move)
+                               break;
+
+                       /* check if we went back home after rotating a number of bytes */
+                       if (to == from)
+                               break;
+
+                       /* if we ended up in the empty area, let's walk to next place. The
+                        * empty area is either between buf->r and from or before from or
+                        * after buf->r.
+                        */
+                       if (from > buf->r) {
+                               if (to >= buf->r && to < from)
+                                       break;
+                       } else if (from < buf->r) {
+                               if (to < from || to >= buf->r)
+                                       break;
+                       }
+
+                       /* we have overwritten a byte of the original set, let's move it */
+                       to += advance;
+                       if (to >= buf->data + buf->size)
+                               to -= buf->size;
+               }
+
+               from++;
+               if (from >= buf->data + buf->size)
+                       from -= buf->size;
+       }
+}
+
+
 /*
  * Does an snprintf() at the end of chunk <chk>, respecting the limit of
  * at most chk->size chars. If the chk->len is over, nothing is added. Returns
index e502bffe1307284af114d0d2b6b8702f8984f976..8816699ac9d61ebdc2b369b1e2ce720ec71031c9 100644 (file)
@@ -2262,18 +2262,11 @@ void http_buffer_heavy_realign(struct buffer *buf, struct http_msg *msg)
         *   - the buffer is in two blocks, we move it via the trash
         */
        if (buf->l) {
-               int block1 = buf->l;
-               int block2 = 0;
-               if (buf->r <= buf->w) {
+               if (buf->r <= buf->w)
                        /* non-contiguous block */
-                       block1 = buf->data + buf->size - buf->w;
-                       block2 = buf->r - buf->data;
-               }
-               if (block2)
-                       memcpy(trash, buf->data, block2);
-               memmove(buf->data, buf->w, block1);
-               if (block2)
-                       memcpy(buf->data + block1, trash, block2);
+                       buffer_bounce_realign(buf);
+               else
+                       memmove(buf->data, buf->w, buf->l);
        }
 
        /* adjust all known pointers */